﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
using System.IO; 
using SGP;

namespace PuzzleMaker
{
    public partial class Form_Main : Form
    {
        enum EditMode { TILES, OBJECTS, WAYPOINTS, COLLISION };
        enum Tool { TILE_PENCIL, TILE_ERASE, TILE_PAINT, TILE_SELECT, TILE_WAND, COLLISION_DRAW, OBJECT_SELECT, WAYPOINT_SELECT };

        public bool RunApp;

        private int mapWidth;
        private int mapHeight;
        private int newMapWidth;
        private int newMapHeight;
        private int tileWidth;
        private int tileHeight;
        private int collisionGridWidth;
        private int collisionGridHeight;
        private int spawnPointID;

        private bool drawGrid;
        private bool drawObjects;
        private bool drawWaypoints;
        private bool drawCollisionBoxes;
        private bool rMouseDown;
        private bool lMouseDown;
        private bool mapSizeChanged;
        private bool showSpawnPoint;
        private bool snapObjectsToGrid;
        private bool snapCollisionBoxesToGrid;

        private Tile[,] tilemap;
        private Tile selectedTile;
        private Tile defaultTile;

        private List<Tileset> tilesets;
        private List<CollisionBox> collisionBoxes;
        private List<GameObject> gameObjects;
        private List<Point> tileSelection;

        private Point tilemapOffset;
        private Point tilesetOffset;
        private Point tilemapMousePosition;
        private Point previousMousePosition;
        private Point spawnPoint;

        private Selection collisionSelection;

        private EditMode editMode;
        private Tool tool;

        private string savePath = "";
        private string workingDirectory = "";

        Random MainRandom = new Random();

        CSGP_Direct3D d3d;
        CSGP_TextureManager tM;

        public Form_Main()
        {
            InitializeComponent();

            RunApp = true;

            d3d = CSGP_Direct3D.GetInstance();
            tM = CSGP_TextureManager.GetInstance();

            d3d.Initialize(panel_Main, true);
            tM.Initialize(d3d.Device, d3d.Sprite);

            d3d.AddRenderTarget(panel_Tileset);

            // Maximize the window on start.
            this.WindowState = FormWindowState.Maximized;

            InitializeTilemap();

            // Create a default blank tilemap.
            ClearMap();

            // Center the map on startup.
            CenterTileMap();
        }

        private void Form_Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            RunApp = false;

            d3d.Terminate();
            tM.Terminate();
        }

        public new void Update()
        {
            
        }

        public void Render()
        {
            d3d.Clear(panel_Main, Color.White);

            d3d.DeviceBegin();
            d3d.SpriteBegin();

            RenderTilemap();
            RenderTileSelection();
            RenderGrid();
            RenderNewMapSize();
            RenderObjects();
            RenderCollisionBoxes();

            d3d.SpriteEnd();
            d3d.DeviceEnd();

            d3d.Present();

            // Only render the tileset preview if the tab is open.
            if(tabControl_Main.SelectedTab.Text == "Tiles")
                RenderTilesetPreview();
        }

        // Renders the tiles.
        private void RenderTilemap()
        {
            // Loop through all tiles.
            for (int x = 0; x < mapWidth; ++x)
            {
                for (int y = 0; y < mapHeight; ++y)
                {
                    // Is the tile empty?
                    if (tilemap[x, y].TilesetID == -1)
                    {
                        // Is there a default tile to render?
                        if (defaultTile.TilesetID != -1)
                        {
                            RenderTile(defaultTile, x, y);
                        }
                    }
                    else
                    {
                        RenderTile(tilemap[x, y], x, y);
                    }
                }
            }

            // Render the spawn point.
            if (showSpawnPoint == true && spawnPointID != -1)
            {
                tM.Draw(spawnPointID, spawnPoint.X + tilemapOffset.X, spawnPoint.Y + tilemapOffset.Y);
            }
        }

        // Renders the selected tiles.
        private void RenderTileSelection()
        {
            Rectangle drawRect = new Rectangle(0, 0, tileWidth, tileHeight);

            for (int i = 0; i < tileSelection.Count; ++i)
            {
                drawRect.X = tileSelection[i].X * tileWidth + tilemapOffset.X;
                drawRect.Y = tileSelection[i].Y * tileHeight + tilemapOffset.Y;

                d3d.DrawRect(drawRect, Color.FromArgb(128 + MainRandom.Next(-5, 5), 255, 255, 255));
            }
        }

        // Render the grid in the main panel.
        private void RenderGrid()
        {
            // Don't render if the grid is disabled.
            if (drawGrid == false)
                return;

            // Render the vertical lines.
            for (int x = tilemapOffset.X; x <= tilemapOffset.X + mapWidth * tileWidth; x += tileWidth)
                d3d.DrawLine(x, tilemapOffset.Y, x, tilemapOffset.Y + mapHeight * tileHeight, Color.Gray, 1);

            // Render the horizontal lines.
            for (int y = tilemapOffset.Y; y <= tilemapOffset.Y + mapHeight * tileHeight; y += tileHeight)
                d3d.DrawLine(tilemapOffset.X, y, tilemapOffset.X + mapWidth * tileWidth, y, Color.Gray, 1);
        }

        // Render all collision boxes and the collision selection box.
        private void RenderCollisionBoxes()
        {
            // Don't render if collision box drawing has been disabled.
            if (drawCollisionBoxes == false)
                return;

            // Render all collision boxes.
            for (int i = 0; i < collisionBoxes.Count; ++i)
            {
                Rectangle rect = collisionBoxes[i].Bounds();
                rect.X += tilemapOffset.X;
                rect.Y += tilemapOffset.Y;

                // If the mouse is over the collision box, highlight it.
                if(PointInRectangle(tilemapMousePosition, rect))
                    d3d.DrawRect(rect, Color.FromArgb(128, 200, 180, 0));
                else
                    d3d.DrawRect(rect, Color.FromArgb(128, 200, 120, 0));

                rect.Width -= 2;
                rect.Height -= 2;

                d3d.DrawHollowRect(rect, Color.FromArgb(128, 220, 100, 0), 1);
            }

            // Render the collision selection box.
            if (collisionSelection.Enabled == true)
            {
                Rectangle rect = new Rectangle();
                rect.X = collisionSelection.Start.X + tilemapOffset.X;
                rect.Y = collisionSelection.Start.Y + tilemapOffset.Y;
                rect.Width = collisionSelection.Width();
                rect.Height = collisionSelection.Height();

                d3d.DrawRect(rect, Color.FromArgb(64, 100, 100, 0));
                d3d.DrawHollowRect(rect, Color.FromArgb(64, 200, 150, 0), 1);
            }
        }

        // Renders a preview of the new map size.
        private void RenderNewMapSize()
        {
            // Don't render if the map size has not been changed.
            if (mapSizeChanged == false)
                return;

            Rectangle rect = new Rectangle();
            rect.X = tilemapOffset.X - ((int)nud_ShiftLeft.Value * tileWidth);
            rect.Y = tilemapOffset.Y - ((int)nud_ShiftUp.Value * tileHeight);
            rect.Width = newMapWidth * tileWidth;
            rect.Height = newMapHeight * tileHeight;

            d3d.DrawHollowRect(rect, Color.Red, 2);
        }

        // Render all objects on the screen.
        private void RenderObjects()
        {
            // Don't render if object rendering is disabled.
            if (drawObjects == false)
                return;

            // Loop through all game objects.
            for (int i = 0; i < gameObjects.Count; ++i)
            {
                int objX = gameObjects[i].X + tilemapOffset.X;
                int objY = gameObjects[i].Y + tilemapOffset.Y;

                if (snapObjectsToGrid == true && lb_Objects.SelectedIndex == i)
                {
                    objX -= gameObjects[i].X % tileWidth;
                    objY -= gameObjects[i].Y % tileHeight;
                }

                tM.Draw(gameObjects[i].ImageID, objX, objY);

                // If the object is currently selected and the OBJECT_SELECTION
                // tool is currently selected, draw a bounding box.
                if (tabControl_Main.SelectedTab.Text == "Objects" && lb_Objects.SelectedIndex == i)
                {
                    Rectangle bounds = gameObjects[i].Bounds;
                    bounds.X += tilemapOffset.X - 3;
                    bounds.Y += tilemapOffset.Y - 3;
                    bounds.Width += 6;
                    bounds.Height += 6;

                    d3d.DrawHollowRect(bounds, Color.Yellow, 3);

                    // Render all of the object's waypoints.
                    RenderObjectWaypoints(gameObjects[i]);
                }
            }
        }

        // Render all of the specified object's waypoints.
        private void RenderObjectWaypoints(GameObject obj)
        {
            // Loop through all of the object's waypoints.
            for (int i = 0; i < obj.Waypoints.Count; ++i)
            {
                // Is there a preceding waypoint?
                if (i > 0)
                {
                    // Draw a line between the two waypoints.
                    d3d.DrawLine(obj.Waypoints[i].X + tilemapOffset.X, obj.Waypoints[i].Y + tilemapOffset.Y,
                        obj.Waypoints[i - 1].X + tilemapOffset.X, obj.Waypoints[i - 1].Y + tilemapOffset.Y, Color.Orange, 2);
                }

                // Create the waypoint box.
                Rectangle box = new Rectangle();
                box.X = obj.Waypoints[i].X - 6 + tilemapOffset.X;
                box.Y = obj.Waypoints[i].Y - 6 + tilemapOffset.Y;
                box.Width = 12;
                box.Height = 12;

                // Is the current waypoint the selected waypoint?
                if (i == obj.SelectedWaypoint)
                {
                    // Draw a selection box around the waypoint.
                    d3d.DrawHollowRect(box, Color.FromArgb(255, 255, 255, 0), 2);
                }
                else
                {
                    // Draw a selection box around the waypoint.
                    d3d.DrawHollowRect(box, Color.FromArgb(255, 255, 0, 0), 2);
                }
            }
        }

        // Renders a preview of the selected tileset.
        private void RenderTilesetPreview()
        {
            d3d.Clear(panel_Tileset, Color.White);

            d3d.DeviceBegin();
            d3d.SpriteBegin();


            if (lb_Tilesets.SelectedIndex != -1)
            {
                // Get the selected tileset.
                Tileset tileset = tilesets[lb_Tilesets.SelectedIndex];

                // Render the image.
                tM.Draw(tileset.ImageID, tilesetOffset.X, tilesetOffset.Y);

                // Render the vertical lines.
                for (int x = tilesetOffset.X; x <= tilesetOffset.X + tileset.ImageWidth; x += tileWidth)
                    d3d.DrawLine(x, tilesetOffset.Y, x, tilesetOffset.Y + tileset.ImageHeight, Color.Black, 1);

                // Render the horizontal lines.
                for (int y = tilesetOffset.Y; y <= tilesetOffset.Y + tileset.ImageHeight; y += tileHeight)
                    d3d.DrawLine(tilesetOffset.X, y, tilesetOffset.X + tileset.ImageWidth, y, Color.Black, 1);

                // Render a selection rectangle for the selected tile.
                if (selectedTile.TilesetID != -1)
                {
                    Rectangle rect = new Rectangle();
                    rect.X = selectedTile.PosX * tileWidth + tilesetOffset.X;
                    rect.Y = selectedTile.PosY * tileHeight + tilesetOffset.Y;
                    rect.Width = tileWidth;
                    rect.Height = tileHeight;

                    d3d.DrawHollowRect(rect, Color.Yellow, 1);
                }
            }


            d3d.SpriteEnd();
            d3d.DeviceEnd();

            d3d.Present();
        }

        // Renders a single tile.
        private void RenderTile(Tile tile, int x, int y)
        {
            if (tile.TilesetID != -1)
            {
                // Create the source rectangle.
                Rectangle src = new Rectangle(tile.PosX * tileWidth, tile.PosY * tileHeight, tileWidth, tileHeight);

                // Draw the tile.
                tM.Draw(tile.TilesetID, x * tileWidth + tilemapOffset.X, y * tileHeight + tilemapOffset.Y, 1.0f, 1.0f, src);
            }
        }

        // Initialize the tilemap to the editor default values.
        private void InitializeTilemap()
        {
            // Set the map size.
            mapWidth = (int)nud_MapWidth.Value;
            mapHeight = (int)nud_MapHeight.Value;
            newMapWidth = mapWidth;
            newMapHeight = mapHeight;

            // Set the tile size.
            tileWidth = (int)nud_TileWidth.Value;
            tileHeight = (int)nud_TileHeight.Value;

            // Check what can be rendered.
            drawGrid = gridToolStripMenuItem.Checked;
            drawObjects = objectsToolStripMenuItem.Checked;
            drawWaypoints = waypointsToolStripMenuItem.Checked;
            drawCollisionBoxes = collisionBoxesToolStripMenuItem.Checked;

            // Set all combo boxes to a default value.
            cb_Tile_PresetWidth.SelectedIndex = 4;
            cb_Tile_PresetHeight.SelectedIndex = 4;

            rMouseDown = false;
            lMouseDown = false;
            mapSizeChanged = false;
            snapObjectsToGrid = cb_SnapObjectToGrid.Checked;

            // Set collision box position increments to the tile dimensions.
            nud_CollisionBoxPositionX.Increment = 1;
            nud_CollisionBoxPositionY.Increment = 1;

            nud_CollisionBoxWidth.Minimum = 1;
            nud_CollisionBoxWidth.Increment = 1;
            nud_CollisionBoxHeight.Minimum = 1;
            nud_CollisionBoxHeight.Increment = 1;

            nud_SpawnPosXL.Increment = tileWidth;
            nud_SpawnPosYL.Increment = tileHeight;

            ts_cb_WandSetting.SelectedIndex = 1;

            collisionSelection = new Selection(tileWidth, tileHeight);
            collisionSelection.SnapToGrid = cb_SnapToCollisionGrid.Checked;

            collisionGridWidth = tileWidth;
            collisionGridHeight = tileHeight;
            nud_CollisionGridWidth.Value = tileWidth;
            nud_CollisionGridHeight.Value = tileHeight;
            snapCollisionBoxesToGrid = cb_SnapToCollisionGrid.Checked;

            if (snapCollisionBoxesToGrid == true)
            {
                nud_CollisionBoxWidth.Increment = collisionGridWidth;
                nud_CollisionBoxHeight.Increment = collisionGridHeight;
            }

            workingDirectory = Properties.Settings.Default.DirectoryPath;

            if (workingDirectory == "")
            {
                workingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\SMART Puzzles";
                Directory.CreateDirectory(workingDirectory);
                Properties.Settings.Default.DirectoryPath = workingDirectory;
            }

            tilesets = new List<Tileset>();
            collisionBoxes = new List<CollisionBox>();
            gameObjects = new List<GameObject>();
            tileSelection = new List<Point>();

            selectedTile = new Tile(-1, -1, -1);
            defaultTile = new Tile(-1, 0, 0);

            spawnPointID = tM.LoadTexture("Images/spawnpoint.png");

            tool = Tool.TILE_PENCIL;
        }

        // Save all of the level data to an XML file.
        private void SaveLevel()
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Filter = "All Files|*.*|Xml File|*.xml";
            dlg.FilterIndex = 2;
            dlg.DefaultExt = ".xml";
            dlg.InitialDirectory = workingDirectory;
            dlg.AddExtension = true;

            if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.Cancel)
                return;

            XmlDocument document = new XmlDocument();
            document.LoadXml("<level></level>");

            XmlElement root = document.DocumentElement;

            // Create the spawn point for the player.
            XmlElement spawnLocation = document.CreateElement("spawnLocation");
            spawnLocation.SetAttribute("x", spawnPoint.X.ToString());
            spawnLocation.SetAttribute("y", spawnPoint.Y.ToString());

            root.AppendChild(spawnLocation);

            //////////////////////
            // Save all objects //
            //////////////////////

            for (int i = 0; i < gameObjects.Count; ++i)
            {
                XmlElement objectElement = document.CreateElement("object");
                objectElement.SetAttribute("id", gameObjects[i].Name);
                objectElement.SetAttribute("tag", gameObjects[i].Tag);
                objectElement.SetAttribute("data", gameObjects[i].Data);
                objectElement.SetAttribute("hasWaypoints", gameObjects[i].UseWaypoints.ToString());
                objectElement.SetAttribute("useEvents", gameObjects[i].UseEvents.ToString());

                XmlElement position = document.CreateElement("position");
                position.SetAttribute("x", gameObjects[i].X.ToString());
                position.SetAttribute("y", gameObjects[i].Y.ToString());
                objectElement.AppendChild(position);

                XmlElement sizeElement = document.CreateElement("size");
                sizeElement.SetAttribute("width", gameObjects[i].Width.ToString());
                sizeElement.SetAttribute("height", gameObjects[i].Height.ToString());
                objectElement.AppendChild(sizeElement);

                if (gameObjects[i].UseWaypoints == true)
                {
                    XmlElement waypointModeElement = document.CreateElement("waypointMode");
                    waypointModeElement.SetAttribute("mode", gameObjects[i].WaypointMode);
                    objectElement.AppendChild(waypointModeElement);

                    for (int n = 0; n < gameObjects[i].Waypoints.Count; ++n)
                    {
                        XmlElement waypointElement = document.CreateElement("waypoint");
                        waypointElement.SetAttribute("x", gameObjects[i].Waypoints[n].X.ToString());
                        waypointElement.SetAttribute("y", gameObjects[i].Waypoints[n].Y.ToString());
                        objectElement.AppendChild(waypointElement);
                    }
                }

                // Save the object's events, if the object uses events.
                if (gameObjects[i].UseEvents == true)
                {
                    for(int j = 0; j < gameObjects[i].Events.Count; ++j)
                    {
                        XmlElement eventElement = document.CreateElement("event");
                        eventElement.SetAttribute("name", gameObjects[i].Events[j].Name);

                        // Add the tags to the event element.
                        for (int k = 0; k < gameObjects[i].Events[j].Tags.Count; ++k)
                        {
                            XmlElement tagElement = document.CreateElement("tag");
                            tagElement.InnerText = gameObjects[i].Events[j].Tags[k];
                            eventElement.AppendChild(tagElement);
                        }

                        objectElement.AppendChild(eventElement);
                    }
                }

                document.DocumentElement.AppendChild(objectElement);
            }

            //////////////////////////////////
            // Save out all collision data. //
            //////////////////////////////////

            for (int i = 0; i < collisionBoxes.Count; ++i)
            {
                XmlElement collisionBox = document.CreateElement("collision");
                collisionBox.SetAttribute("tag", collisionBoxes[i].Tag);
                collisionBox.SetAttribute("canBeGrappled", collisionBoxes[i].CanGrappleAttach.ToString());

                XmlElement position = document.CreateElement("position");
                position.SetAttribute("x", collisionBoxes[i].X.ToString());
                position.SetAttribute("y", collisionBoxes[i].Y.ToString());

                XmlElement size = document.CreateElement("size");
                size.SetAttribute("width", collisionBoxes[i].Width.ToString());
                size.SetAttribute("height", collisionBoxes[i].Height.ToString());

                collisionBox.AppendChild(position);
                collisionBox.AppendChild(size);
                root.AppendChild(collisionBox);
            }

            // Save the tilemap.
            XmlElement xmlTilemap = document.CreateElement("tileMap");
            xmlTilemap.SetAttribute("width", mapWidth.ToString());
            xmlTilemap.SetAttribute("height", mapHeight.ToString());
            xmlTilemap.SetAttribute("tileWidth", tileWidth.ToString());
            xmlTilemap.SetAttribute("tileHeight", tileHeight.ToString());

            // Save out the tilesets.
            for (int i = 0; i < tilesets.Count; ++i)
            {
                
                XmlElement xmlTileset = document.CreateElement("tileSet");
                xmlTileset.SetAttribute("image", Path.GetFileName(tilesets[i].FileName));
                xmlTileset.SetAttribute("tileID", tilesets[i].ImageID.ToString());

                xmlTilemap.AppendChild(xmlTileset);
            }

            // Save the tiles.
            for (int y = 0; y < mapHeight; ++y)
            {
                for (int x = 0; x < mapWidth; ++x)
                {
                    XmlElement xmlTile = document.CreateElement("tile");
                    xmlTile.SetAttribute("x", (x * tileWidth).ToString());
                    xmlTile.SetAttribute("y", (y * tileHeight).ToString());

                    if (tilemap[x, y].TilesetID != -1)
                    {
                        xmlTile.SetAttribute("srcX", (tilemap[x, y].PosX * tileWidth).ToString());
                        xmlTile.SetAttribute("srcY", (tilemap[x, y].PosY * tileHeight).ToString());
                    }
                    else if (defaultTile.TilesetID != -1)
                    {
                        xmlTile.SetAttribute("srcX", (defaultTile.PosX * tileWidth).ToString());
                        xmlTile.SetAttribute("srcY", (defaultTile.PosY * tileHeight).ToString());
                    }
                    else
                    {
                        xmlTile.SetAttribute("srcX", "-1");
                        xmlTile.SetAttribute("srcY", "-1");
                    }

                    if (tilemap[x, y].TilesetID != -1)
                        xmlTile.SetAttribute("id", tilemap[x, y].TilesetID.ToString());
                    else if (defaultTile.TilesetID != -1)
                        xmlTile.SetAttribute("id", defaultTile.TilesetID.ToString());
                    else
                        xmlTile.SetAttribute("id", "-1");

                    xmlTilemap.AppendChild(xmlTile);
                }
            }

            root.AppendChild(xmlTilemap);

            document.Save(dlg.FileName);
        }

        // Save the entire puzzle file.
        private void SavePuzzle()
        {
            DialogResult dlgResult = System.Windows.Forms.DialogResult.OK;

            // Is this the first time that the file has been saved?
            if (savePath == "")
            {
                SaveFileDialog dlg = new SaveFileDialog();
                dlg.Filter = "Puzzle|*.pzl";
                dlg.FilterIndex = 1;
                dlg.DefaultExt = ".pzl";
                dlg.InitialDirectory = workingDirectory;
                dlg.AddExtension = true;

                dlgResult = dlg.ShowDialog();

                // Save the chosen path.
                if (dlgResult == System.Windows.Forms.DialogResult.OK)
                    savePath = dlg.FileName;
            }

            // If the user canceled the save process, do nothing and return.
            if (dlgResult == System.Windows.Forms.DialogResult.Cancel)
                return;

            // Create the document.
            XmlDocument document = new XmlDocument();
            document.LoadXml("<root></root>");

            // Create the spawn point for the player.
            XmlElement spawnLocation = document.CreateElement("spawnLocation");
            spawnLocation.SetAttribute("x", spawnPoint.X.ToString());
            spawnLocation.SetAttribute("y", spawnPoint.Y.ToString());

            document.DocumentElement.AppendChild(spawnLocation);

            //////////////////////////////////
            // Save all of the map settings //
            //////////////////////////////////

            XmlElement mapSettingsElement = document.CreateElement("mapSettings");

            // Save the size of the map.
            XmlElement mapSizeElement = document.CreateElement("mapSize");
            mapSizeElement.SetAttribute("width", mapWidth.ToString());
            mapSizeElement.SetAttribute("height", mapHeight.ToString());
            mapSettingsElement.AppendChild(mapSizeElement);

            // Save the tile size.
            XmlElement tileSizeElement = document.CreateElement("tileSize");
            tileSizeElement.SetAttribute("width", tileWidth.ToString());
            tileSizeElement.SetAttribute("height", tileHeight.ToString());
            mapSettingsElement.AppendChild(tileSizeElement);

            // Save the map offset.
            XmlElement mapOffsetElement = document.CreateElement("mapOffset");
            mapOffsetElement.SetAttribute("x", tilemapOffset.X.ToString());
            mapOffsetElement.SetAttribute("y", tilemapOffset.Y.ToString());
            mapSettingsElement.AppendChild(mapOffsetElement);

            // Save what is supposed to be rendered.
            XmlElement renderSettingsElement = document.CreateElement("renderSettings");
            renderSettingsElement.SetAttribute("drawGrid", drawGrid.ToString());
            renderSettingsElement.SetAttribute("drawObjects", drawObjects.ToString());
            renderSettingsElement.SetAttribute("drawCollisionBoxes", drawCollisionBoxes.ToString());
            renderSettingsElement.SetAttribute("drawWaypoints", drawWaypoints.ToString());
            mapSettingsElement.AppendChild(renderSettingsElement);

            document.DocumentElement.AppendChild(mapSettingsElement);

            // Create the spawn point for the player.
            XmlElement spawnLocationElement = document.CreateElement("spawnLocation");
            spawnLocationElement.SetAttribute("x", "0");
            spawnLocationElement.SetAttribute("y", "0");
            document.DocumentElement.AppendChild(spawnLocation);

            //////////////////////
            // Save all objects //
            //////////////////////

            for (int i = 0; i < gameObjects.Count; ++i)
            {
                XmlElement objectElement = document.CreateElement("object");
                objectElement.SetAttribute("id", gameObjects[i].Name);
                objectElement.SetAttribute("tag", gameObjects[i].Tag);
                objectElement.SetAttribute("data", gameObjects[i].Data);
                objectElement.SetAttribute("hasWaypoints", gameObjects[i].UseWaypoints.ToString());
                objectElement.SetAttribute("useEvents", gameObjects[i].UseEvents.ToString());

                XmlElement position = document.CreateElement("position");
                position.SetAttribute("x", gameObjects[i].X.ToString());
                position.SetAttribute("y", gameObjects[i].Y.ToString());
                objectElement.AppendChild(position);

                XmlElement sizeElement = document.CreateElement("size");
                sizeElement.SetAttribute("width", gameObjects[i].Width.ToString());
                sizeElement.SetAttribute("height", gameObjects[i].Height.ToString());
                objectElement.AppendChild(sizeElement);

                if (gameObjects[i].UseWaypoints == true)
                {
                    XmlElement waypointModeElement = document.CreateElement("waypointMode");
                    waypointModeElement.SetAttribute("mode", gameObjects[i].WaypointMode);
                    objectElement.AppendChild(waypointModeElement);

                    for (int n = 0; n < gameObjects[i].Waypoints.Count; ++n)
                    {
                        XmlElement waypointElement = document.CreateElement("waypoint");
                        waypointElement.SetAttribute("x", gameObjects[i].Waypoints[n].X.ToString());
                        waypointElement.SetAttribute("y", gameObjects[i].Waypoints[n].Y.ToString());
                        objectElement.AppendChild(waypointElement);
                    }
                }

                // Save the object's events, if the object uses events.
                if (gameObjects[i].UseEvents == true)
                {
                    for (int j = 0; j < gameObjects[i].Events.Count; ++j)
                    {
                        XmlElement eventElement = document.CreateElement("event");
                        eventElement.SetAttribute("name", gameObjects[i].Events[j].Name);

                        // Add the tags to the event element.
                        for (int k = 0; k < gameObjects[i].Events[j].Tags.Count; ++k)
                        {
                            XmlElement tagElement = document.CreateElement("tag");
                            tagElement.InnerText = gameObjects[i].Events[j].Tags[k];
                            eventElement.AppendChild(tagElement);
                        }

                        objectElement.AppendChild(eventElement);
                    }
                }

                document.DocumentElement.AppendChild(objectElement);
            }

            /////////////////////////////////
            // Save out all collision data //
            /////////////////////////////////

            for (int i = 0; i < collisionBoxes.Count; ++i)
            {
                XmlElement collisionBox = document.CreateElement("collision");
                collisionBox.SetAttribute("name", collisionBoxes[i].Name);
                collisionBox.SetAttribute("tag", collisionBoxes[i].Tag);
                collisionBox.SetAttribute("canBeGrappled", collisionBoxes[i].CanGrappleAttach.ToString());

                XmlElement position = document.CreateElement("position");
                position.SetAttribute("x", collisionBoxes[i].X.ToString());
                position.SetAttribute("y", collisionBoxes[i].Y.ToString());

                XmlElement size = document.CreateElement("size");
                size.SetAttribute("width", collisionBoxes[i].Width.ToString());
                size.SetAttribute("height", collisionBoxes[i].Height.ToString());

                collisionBox.AppendChild(position);
                collisionBox.AppendChild(size);
                document.DocumentElement.AppendChild(collisionBox);
            }

            //////////////////////
            // Save the tilemap //
            //////////////////////

            XmlElement xmlTilemap = document.CreateElement("tileMap");
            xmlTilemap.SetAttribute("width", mapWidth.ToString());
            xmlTilemap.SetAttribute("height", mapHeight.ToString());
            xmlTilemap.SetAttribute("tileWidth", tileWidth.ToString());
            xmlTilemap.SetAttribute("tileHeight", tileHeight.ToString());

            ///////////////////////////
            // Save out the tilesets //
            ///////////////////////////

            int deltaImageID = 0;

            for (int i = 0; i < tilesets.Count; ++i)
            {
                if (i == 0)
                    deltaImageID = tilesets[i].ImageID;

                XmlElement xmlTileset = document.CreateElement("tileSet");
                xmlTileset.SetAttribute("image", tilesets[i].FileName);
                xmlTileset.SetAttribute("tileID", i.ToString());

                string tempFilepath = Path.GetDirectoryName(savePath) + "\\Puzzle Images\\";
                Directory.CreateDirectory(tempFilepath);
                tempFilepath += tilesets[i].FileName;
                try
                {
                    tilesets[i].Image.Save(tempFilepath);
                }
                catch(Exception) {}

                xmlTilemap.AppendChild(xmlTileset);
            }

            ////////////////////
            // Save the tiles //
            ////////////////////

            if (defaultTile.TilesetID != -1)
            {
                XmlElement xmlDefaultTile = document.CreateElement("defaultTile");
                xmlDefaultTile.SetAttribute("x", defaultTile.PosX.ToString());
                xmlDefaultTile.SetAttribute("y", defaultTile.PosY.ToString());
                xmlDefaultTile.SetAttribute("id", (defaultTile.TilesetID - deltaImageID).ToString());
                xmlTilemap.AppendChild(xmlDefaultTile);
            }

            for (int y = 0; y < mapHeight; ++y)
            {
                for (int x = 0; x < mapWidth; ++x)
                {
                    XmlElement xmlTile = document.CreateElement("tile");
                    xmlTile.SetAttribute("x", (x * tileWidth).ToString());
                    xmlTile.SetAttribute("y", (y * tileHeight).ToString());

                    if (tilemap[x, y].TilesetID != -1)
                    {
                        xmlTile.SetAttribute("srcX", (tilemap[x, y].PosX * tileWidth).ToString());
                        xmlTile.SetAttribute("srcY", (tilemap[x, y].PosY * tileHeight).ToString());
                    }
                    else if (defaultTile.TilesetID != -1)
                    {
                        xmlTile.SetAttribute("srcX", (defaultTile.PosX * tileWidth).ToString());
                        xmlTile.SetAttribute("srcY", (defaultTile.PosY * tileHeight).ToString());
                    }
                    else
                    {
                        xmlTile.SetAttribute("srcX", "-1");
                        xmlTile.SetAttribute("srcY", "-1");
                    }

                    if (tilemap[x, y].TilesetID != -1)
                        xmlTile.SetAttribute("id", (tilemap[x, y].TilesetID - deltaImageID).ToString());
                    else if (defaultTile.TilesetID != -1)
                        xmlTile.SetAttribute("id", (defaultTile.TilesetID - deltaImageID).ToString());
                    else
                        xmlTile.SetAttribute("id", "-1");

                    xmlTilemap.AppendChild(xmlTile);
                }
            }

            document.DocumentElement.AppendChild(xmlTilemap);

            // Save the document.
            document.Save(savePath);
        }

        // Load an entire puzzle from a file.
        private void OpenPuzzle()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "Puzzle|*.pzl";
            dlg.FilterIndex = 1;
            dlg.InitialDirectory = workingDirectory;

            // If the user cancels, do nothing and return.
            if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.Cancel)
                return;

            XDocument document = XDocument.Load(dlg.FileName);

            XElement root = document.Root;

            // Load the spawn point.
            XElement spawnElement = root.Element("spawnLocation");
            spawnPoint.X = Convert.ToInt32(spawnElement.Attribute("x").Value);
            spawnPoint.Y = Convert.ToInt32(spawnElement.Attribute("y").Value);

            // Load the map settings.
            XElement mapSettingsElement = root.Element("mapSettings");
            if (mapSettingsElement != null)
            {
                XElement mapSizeElement = mapSettingsElement.Element("mapSize");
                XElement tileSizeElement = mapSettingsElement.Element("tileSize");
                XElement mapOffsetElement = mapSettingsElement.Element("mapOffset");
                XElement renderSettingsElement = mapSettingsElement.Element("renderSettings");

                // Set the map size.
                nud_MapWidth.Value = Convert.ToDecimal(mapSizeElement.Attribute("width").Value);
                nud_MapHeight.Value = Convert.ToDecimal(mapSizeElement.Attribute("height").Value);
                btn_Apply_Click(null, null);

                // Set the tile size.
                nud_TileWidth.Value = Convert.ToDecimal(tileSizeElement.Attribute("width").Value);
                nud_TileHeight.Value = Convert.ToDecimal(tileSizeElement.Attribute("height").Value);

                // Set the map offset.
                tilemapOffset.X = Convert.ToInt32(mapOffsetElement.Attribute("x").Value);
                tilemapOffset.Y = Convert.ToInt32(mapOffsetElement.Attribute("y").Value);

                // Set the render settings.
                drawGrid = Convert.ToBoolean(renderSettingsElement.Attribute("drawGrid").Value);
                drawObjects = Convert.ToBoolean(renderSettingsElement.Attribute("drawObjects").Value);
                drawCollisionBoxes = Convert.ToBoolean(renderSettingsElement.Attribute("drawCollisionBoxes").Value);
                drawWaypoints = Convert.ToBoolean(renderSettingsElement.Attribute("drawWaypoints").Value);

                gridToolStripMenuItem.Checked = drawGrid;
                objectsToolStripMenuItem.Checked = drawObjects;
                collisionBoxesToolStripMenuItem.Checked = drawCollisionBoxes;
                waypointsToolStripMenuItem.Checked = drawWaypoints;

                btn_Apply_Click(null, null);

                // Create an empty map.
                ClearMap();
            }

            /////////////////////////////
            // Load the tileset images //
            /////////////////////////////

            XElement tileMapElement = root.Element("tileMap");
            if (tileMapElement != null)
            {
                XElement tileSetElement = tileMapElement.Element("tileSet");
                XElement tileElement = tileMapElement.Element("tile");

                while (tileSetElement.Name == "tileSet")
                {
                    // Load the texture.

                    string loadPath = Path.GetDirectoryName(dlg.FileName) + "\\Puzzle Images\\";
                    loadPath += tileSetElement.Attribute("image").Value;
                    LoadTilesetImage(loadPath);

                    tileSetElement = tileSetElement.ElementsAfterSelf().First<XElement>();
                }

                // Load all tiles.

                XElement defaultTileElement = tileMapElement.Element("defaultTile");
                if (defaultTileElement != null)
                {
                    defaultTile.PosX = Convert.ToInt32(defaultTileElement.Attribute("x").Value);
                    defaultTile.PosY = Convert.ToInt32(defaultTileElement.Attribute("y").Value);
                    defaultTile.TilesetID = Convert.ToInt32(defaultTileElement.Attribute("id").Value);
                }

                bool loadingTiles = true;
                while (loadingTiles == true)
                {
                    int x = Convert.ToInt32(tileElement.Attribute("x").Value) / tileWidth;
                    int y = Convert.ToInt32(tileElement.Attribute("y").Value) / tileHeight;

                    int id = Convert.ToInt32(tileElement.Attribute("id").Value);
                    if (id != -1)
                    {
                        tilemap[x, y].TilesetID = tilesets[id].ImageID;
                        tilemap[x, y].PosX = Convert.ToInt32(tileElement.Attribute("srcX").Value) / tileWidth;
                        tilemap[x, y].PosY = Convert.ToInt32(tileElement.Attribute("srcY").Value) / tileHeight;
                    }

                    if (id == defaultTile.TilesetID && tilemap[x, y].PosX == defaultTile.PosX && tilemap[x, y].PosY == defaultTile.PosY)
                    {
                        tilemap[x, y].TilesetID = -1;
                        tilemap[x, y].PosX = -1;
                        tilemap[x, y].PosY = -1;
                    }

                    IEnumerable<XElement> siblings = tileElement.ElementsAfterSelf();

                    if (siblings.Count<XElement>() != 0)
                        tileElement = siblings.First<XElement>();
                    else
                        loadingTiles = false;
                }
            }

            /////////////////////////////
            // Load all of the objects //
            /////////////////////////////

            XElement objectElement = root.Element("object");
            bool loadingObjects = objectElement != null;
            while (loadingObjects == true && objectElement.Name == "object")
            {
                string name = objectElement.Attribute("id").Value;

                CreateObject(name);

                GameObject obj = gameObjects[gameObjects.Count - 1];

                obj.Tag = objectElement.Attribute("tag").Value;

                var data = objectElement.Attribute("data");
                if (data != null)
                    obj.Data = data.Value;

                XElement objPosition = objectElement.Element("position");
                obj.X = Convert.ToInt32(objPosition.Attribute("x").Value);
                obj.Y = Convert.ToInt32(objPosition.Attribute("y").Value);

                XElement objSize = objectElement.Element("size");
                obj.Width = Convert.ToInt32(objSize.Attribute("width").Value);
                obj.Height = Convert.ToInt32(objSize.Attribute("height").Value);

                // Load all of the object's waypoints.
                if (Convert.ToBoolean(objectElement.Attribute("hasWaypoints").Value) == true)
                {
                    obj.UseWaypoints = true;

                    XElement waypointModeElement = objectElement.Element("waypointMode");
                    obj.WaypointMode = waypointModeElement.Attribute("mode").Value;

                    XElement waypointElement = objectElement.Element("waypoint");
                    bool loadingWaypoints = true;
                    while (loadingWaypoints == true && waypointElement.Name == "waypoint")
                    {
                        Point waypoint = new Point();
                        waypoint.X = Convert.ToInt32(waypointElement.Attribute("x").Value);
                        waypoint.Y = Convert.ToInt32(waypointElement.Attribute("y").Value);
                        obj.Waypoints.Add(waypoint);

                        IEnumerable<XElement> s = waypointElement.ElementsAfterSelf();

                        if (s.Count<XElement>() != 0)
                            waypointElement = s.First<XElement>();
                        else
                            loadingWaypoints = false;
                    }

                    UpdateObjectWaypoints();
                }
                
                if (Convert.ToBoolean(objectElement.Attribute("useEvents").Value) == true)
                {
                    obj.UseEvents = true;

                    XElement eventElement = objectElement.Element("event");

                    bool loadingEvents = true;
                    while (loadingEvents == true && eventElement != null && eventElement.Name == "event")
                    {
                        Event e = new Event(eventElement.Attribute("name").Value);

                        XElement tagElement = eventElement.Element("tag");

                        bool loadingTags = true;
                        while (loadingTags == true && tagElement != null && tagElement.Name == "tag")
                        {
                            e.Tags.Add(tagElement.Value);

                            IEnumerable<XElement> s = tagElement.ElementsAfterSelf();
                            if (s.Count<XElement>() != 0)
                                tagElement = s.First<XElement>();
                            else
                                loadingTags = false;
                        }

                        obj.Events.Add(e);

                        IEnumerable<XElement> sib = eventElement.ElementsAfterSelf();
                        if (sib.Count<XElement>() != 0)
                            eventElement = sib.First<XElement>();
                        else
                            loadingEvents = false;
                    }

                    UpdateObjectEventList(obj);
                }

                IEnumerable<XElement> siblings = objectElement.ElementsAfterSelf();

                if (siblings.Count<XElement>() != 0)
                    objectElement = siblings.First<XElement>();
                else
                    loadingObjects = false;
            }

            UpdateObjectList();

            //////////////////////////////
            // Load the collision boxes //
            //////////////////////////////

            XElement collisionBoxElement = root.Element("collision");

            bool loadingCollisionBoxes = true;
            while (loadingCollisionBoxes == true && collisionBoxElement != null && collisionBoxElement.Name == "collision")
            {
                CollisionBox box = new CollisionBox();

                var CheckName = collisionBoxElement.Attribute("name");
                if (CheckName != null)
                    box.Name = CheckName.Value;
                box.Tag = collisionBoxElement.Attribute("tag").Value;
                box.CanGrappleAttach = Convert.ToBoolean(collisionBoxElement.Attribute("canBeGrappled").Value);

                XElement position = collisionBoxElement.Element("position");
                XElement size = collisionBoxElement.Element("size");

                box.X = Convert.ToInt32(position.Attribute("x").Value);
                box.Y = Convert.ToInt32(position.Attribute("y").Value);

                box.Width = Convert.ToInt32(size.Attribute("width").Value);
                box.Height = Convert.ToInt32(size.Attribute("height").Value);

                collisionBoxes.Add(box);

                IEnumerable<XElement> siblings = collisionBoxElement.ElementsAfterSelf();

                if (siblings.Count<XElement>() != 0)
                    collisionBoxElement = siblings.First<XElement>();
                else
                    loadingCollisionBoxes = false;
            }

            UpdateCollisionBoxList();
        }

        // Helper function to load in a tileset image.
        private void LoadTilesetImage(string filename)
        {
            // Create a tileset from the image
            Tileset tileset = new Tileset();
            tileset.Image = Image.FromFile(filename);
            tileset.ImageID = -1;
            tileset.ImageID = tM.LoadTexture(filename);
            tileset.Name = "Tile Set " + (lb_Tilesets.Items.Count + 1).ToString();
            tileset.FileName = Path.GetFileName(filename);
            tileset.ImageWidth = tileset.Image.Width;
            tileset.ImageHeight = tileset.Image.Height;
            tileset.TileWidth = tileWidth;
            tileset.TileHeight = tileHeight;
            tileset.Rows = tileset.Image.Width / tileWidth;
            tileset.Columns = tileset.Image.Height / tileHeight;

            // Add the tileset to the list box.
            lb_Tilesets.Items.Add(tileset);

            // Update the tileset list.
            UpdateTilesetList();

            if (lb_Tilesets.SelectedIndex == -1)
                lb_Tilesets.SelectedIndex = 0;
        }

        // Creates a new, empty map.
        public void ClearMap( )
        {
            tilemap = new Tile[mapWidth, mapHeight];

            // Loop through all tiles.
            for (int x = 0; x < mapWidth; ++x)
            {
                for (int y = 0; y < mapHeight; ++y)
                {
                    tilemap[x, y] = new Tile(-1, -1, -1);
                }
            }

            // Change settings for all current tile sets.
            for (int i = 0; i < lb_Tilesets.Items.Count; ++i)
            {
                Tileset tileset = (Tileset)lb_Tilesets.Items[lb_Tilesets.SelectedIndex];

                // Calculate the new row and column count.
                tileset.Rows = tileset.ImageHeight / tileHeight;
                tileset.Columns = tileset.ImageWidth / tileWidth;

                // Reset the selected tile.
                selectedTile = new Tile();

                lb_Tilesets.Items[lb_Tilesets.SelectedIndex] = tileset;
            }
        }

        // Resizes the map.
        public void ResizeMap(int width, int height)
        {
            // Create a new temporary map to copy the existing tiles to.
            Tile[,] tempTilemap = new Tile[width, height];

            // Get the shifted region amount.
            int shiftLeft = (int)nud_ShiftLeft.Value;
            int shiftUp = (int)nud_ShiftUp.Value;

            // Copy existing tiles.
            for (int x = 0; x < width; ++x)
            {
                for (int y = 0; y < height; ++y)
                {
                    // Does the tile exist within the tilemap?
                    if (x - shiftLeft >= 0 && x - shiftLeft < tilemap.GetLength(0) &&
                        y - shiftUp >= 0 && y - shiftUp < tilemap.GetLength(1))
                    {
                        tempTilemap[x, y] = new Tile(tilemap[x - shiftLeft, y - shiftUp].TilesetID,
                            tilemap[x - shiftLeft, y - shiftUp].PosX, tilemap[x - shiftLeft, y - shiftUp].PosY);
                    }
                    else
                    {
                        tempTilemap[x, y] = new Tile(-1, x, y);
                    }
                }
            }

            tilemap = tempTilemap;
        }

        // Places a tile on the tile map.
        public void PlaceTile(MouseEventArgs e)
        {
            // Is a tile selected for placement?
            if (selectedTile.TilesetID != -1)
            {
                // Calculate the selection position.
                Point selectPos = e.Location;
                selectPos.X -= tilemapOffset.X;
                selectPos.Y -= tilemapOffset.Y;

                int tileX = selectPos.X / tileWidth;
                int tileY = selectPos.Y / tileHeight;

                if (tileX >= 0 && tileX < mapWidth)
                {
                    if (tileY >= 0 && tileY < mapHeight)
                    {
                        // Copy the selected tile's data.
                        tilemap[tileX, tileY].TilesetID = selectedTile.TilesetID;
                        tilemap[tileX, tileY].PosX = selectedTile.PosX;
                        tilemap[tileX, tileY].PosY = selectedTile.PosY;
                    }
                }
            }
        }

        // Removes a tile from the tile map.
        public void RemoveTile(MouseEventArgs e)
        {
            // Calculate the selection position.
            Point selectPos = e.Location;
            selectPos.X -= tilemapOffset.X;
            selectPos.Y -= tilemapOffset.Y;

            int tileX = selectPos.X / tileWidth;
            int tileY = selectPos.Y / tileHeight;

            if (tileX >= 0 && tileX < mapWidth)
            {
                if (tileY >= 0 && tileY < mapHeight)
                {
                    // Is the tile NOT empty?
                    if (tilemap[tileX, tileY] != null)
                    {
                        // Nullify the tile.
                        tilemap[tileX, tileY].TilesetID = -1;
                    }
                }
            }
        }

        // Create the object and place it on screen.
        private void CreateObject(string name)
        {
            GameObject obj = new GameObject(name);
            obj.Position = CenterScreenLocation();

            if (name == "Button")
            {
                obj.ImageID = tM.LoadTexture("Images/button.png");
                obj.Width = 96;
                obj.Height = 9;
            }
            else if (name == "Conveyor Left")
            {
                obj.ImageID = tM.LoadTexture("Images/conveyor left.png");
                obj.Width = obj.Height = 32;
            }
            else if (name == "Conveyor Right")
            {
                obj.ImageID = tM.LoadTexture("Images/conveyor right.png");
                obj.Width = obj.Height = 32;
            }
            else if (name == "Conveyor Bottom")
            {
                obj.ImageID = tM.LoadTexture("Images/conveyor bottom.png");
                obj.Width = obj.Height = 32;
            }
            else if (name == "Conveyor Top")
            {
                obj.ImageID = tM.LoadTexture("Images/conveyor top.png");
                obj.Width = obj.Height = 32;
            }
            else if (name == "Conveyor Center Vertical")
            {
                obj.ImageID = tM.LoadTexture("Images/conveyor center vertical.png");
                obj.Width = obj.Height = 32;
            }
            else if (name == "Conveyor Center")
            {
                obj.ImageID = tM.LoadTexture("Images/conveyor center.png");
                obj.Width = obj.Height = 32;
            }
            else if (name == "Crusher")
            {
                obj.ImageID = tM.LoadTexture("Images/crusher.png");
                obj.Width = 64;
                obj.Height = 64;
            }
            else if (name == "Fan Left")
            {
                obj.ImageID = tM.LoadTexture("Images/fan left.png");
                obj.Width = 96;
                obj.Height = 64;
            }
            else if (name == "Fan Right")
            {
                obj.ImageID = tM.LoadTexture("Images/fan right.png");
                obj.Width = 96;
                obj.Height = 64;
            }
            else if (name == "Fan Top")
            {
                obj.ImageID = tM.LoadTexture("Images/fan top.png");
                obj.Width = 96;
                obj.Height = 64;
            }
            else if (name == "Fan Bottom")
            {
                obj.ImageID = tM.LoadTexture("Images/fan bottom.png");
                obj.Width = 96;
                obj.Height = 64;
            }
            else if (name == "Flame Jet Left")
            {
                obj.ImageID = tM.LoadTexture("Images/flame jet left.png");
                obj.Width = 60;
                obj.Height = 19;
            }
            else if (name == "Flame Jet Right")
            {
                obj.ImageID = tM.LoadTexture("Images/flame jet right.png");
                obj.Width = 60;
                obj.Height = 19;
            }
            else if (name == "Flame Jet Top")
            {
                obj.ImageID = tM.LoadTexture("Images/flame jet top.png");
                obj.Width = 19;
                obj.Height = 60;
            }
            else if (name == "Flame Jet Bottom")
            {
                obj.ImageID = tM.LoadTexture("Images/flame jet bottom.png");
                obj.Width = 19;
                obj.Height = 60;
            }
            else if (name == "Generator")
            {
                obj.ImageID = tM.LoadTexture("Images/GenPowered.png");
                obj.Width = 64;
                obj.Height = 64;
            }
            else if (name == "Gate (Verticle)")
            {
                obj.ImageID = tM.LoadTexture("Images/gate.png");
                obj.Width = 8;
                obj.Height = 128;
            }
            else if (name == "Gate (Horizontal)")
            {
                obj.ImageID = tM.LoadTexture("Images/gate horizontal.png");
                obj.Width = 128;
                obj.Height = 8;
            }
            else if (name == "Gate (Conditional)")
            {
                obj.ImageID = tM.LoadTexture("Images/gate conditional.png");
                obj.Width = 8;
                obj.Height = 128;
            }
            else if (name == "Hologram")
            {
                obj.ImageID = tM.LoadTexture("Images/hologram.png");
                obj.Width = 256;
                obj.Height = 358;
            }
            else if (name == "Ice Block")
            {
                obj.ImageID = tM.LoadTexture("Images/ice block.png");
                obj.Width = obj.Height = 64;
            }
            else if (name == "Ice Layer Left")
            {
                obj.ImageID = tM.LoadTexture("Images/ice layer left.png");
                obj.Width = 32;
                obj.Height = 5;
            }
            else if (name == "Ice Layer Right")
            {
                obj.ImageID = tM.LoadTexture("Images/ice layer right.png");
                obj.Width = 32;
                obj.Height = 5;
            }
            else if (name == "Ice Layer Center")
            {
                obj.ImageID = tM.LoadTexture("Images/ice layer center.png");
                obj.Width = 32;
                obj.Height = 5;
            }
            else if (name == "Laser Emitter Bottom")
            {
                obj.ImageID = tM.LoadTexture("Images/laser emitter bottom.png");
                obj.Width = 32;
                obj.Height = 9;
            }
            else if (name == "Laser Emitter Top")
            {
                obj.ImageID = tM.LoadTexture("Images/laser emitter top.png");
                obj.Width = 32;
                obj.Height = 9;
            }
            else if (name == "Laser Emitter Left")
            {
                obj.ImageID = tM.LoadTexture("Images/laser emitter left.png");
                obj.Width = 9;
                obj.Height = 32;
            }
            else if (name == "Laser Emitter Right")
            {
                obj.ImageID = tM.LoadTexture("Images/laser emitter right.png");
                obj.Width = 9;
                obj.Height = 32;
            }
            else if (name == "Laser Receiver Bottom")
            {
                obj.ImageID = tM.LoadTexture("Images/laser receiver bottom.png");
                obj.Width = 32;
                obj.Height = 9;
            }
            else if (name == "Laser Receiver Top")
            {
                obj.ImageID = tM.LoadTexture("Images/laser receiver top.png");
                obj.Width = 32;
                obj.Height = 9;
            }
            else if (name == "Laser Receiver Left")
            {
                obj.ImageID = tM.LoadTexture("Images/laser receiver left.png");
                obj.Width = 9;
                obj.Height = 32;
            }
            else if (name == "Laser Receiver Right")
            {
                obj.ImageID = tM.LoadTexture("Images/laser receiver right.png");
                obj.Width = 9;
                obj.Height = 32;
            }
            else if (name == "Laser Reflector Top Left")
            {
                obj.ImageID = tM.LoadTexture("Images/laser reflector top left.png");
                obj.Width = obj.Height = 32;
            }
            else if (name == "Laser Reflector Top Right")
            {
                obj.ImageID = tM.LoadTexture("Images/laser reflector top right.png");
                obj.Width = obj.Height = 32;
            }
            else if (name == "Laser Reflector Bottom Left")
            {
                obj.ImageID = tM.LoadTexture("Images/laser reflector bottom left.png");
                obj.Width = obj.Height = 32;
            }
            else if (name == "Laser Reflector Bottom Right")
            {
                obj.ImageID = tM.LoadTexture("Images/laser reflector bottom right.png");
                obj.Width = obj.Height = 32;
            }
            else if (name == "Machine Broken")
            {
                obj.ImageID = tM.LoadTexture("Images/machine broken.png");
                obj.Width = 64;
                obj.Height = 64;
            }
            else if (name == "Machine Fixed")
            {
                obj.ImageID = tM.LoadTexture("Images/machine fixed.png");
                obj.Width = 128;
                obj.Height = 121;
            }
            else if (name == "Metal Box")
            {
                obj.ImageID = tM.LoadTexture("Images/metal box.png");
                obj.Width = obj.Height = 64;
            }
            else if (name == "Moving Platform")
            {
                obj.ImageID = tM.LoadTexture("Images/moving platform.png");
                obj.Width = 128;
                obj.Height = 32;
            }
            else if (name == "Particle Emitter")
            {
                obj.ImageID = tM.LoadTexture("Images/particle.png");
                obj.Width = obj.Height = 64;
            }
            else if (name == "Puddle Left")
            {
                obj.ImageID = tM.LoadTexture("Images/puddle left.png");
                obj.Width = 32;
                obj.Height = 5;
            }
            else if (name == "Puddle Right")
            {
                obj.ImageID = tM.LoadTexture("Images/puddle right.png");
                obj.Width = 32;
                obj.Height = 5;
            }
            else if (name == "Puddle Center")
            {
                obj.ImageID = tM.LoadTexture("Images/puddle center.png");
                obj.Width = 32;
                obj.Height = 5;
            }
            else if (name == "Radiation Barrel")
            {
                obj.ImageID = tM.LoadTexture("Images/radiation barrel.png");
                obj.Width = 64;
                obj.Height = 96;
            }
            else if (name == "Saw")
            {
                obj.ImageID = tM.LoadTexture("Images/saw.png");
                obj.Width = 128;
                obj.Height = 64;
            }
            else if (name == "Security Cannon")
            {
                obj.ImageID = tM.LoadTexture("Images/security cannon.png");
                obj.Width = obj.Height = 64;
            }
            else if (name == "Trip Wire Left")
            {
                obj.ImageID = tM.LoadTexture("Images/trip wire left.png");
                obj.Width = 16;
                obj.Height = 32;
            }
            else if (name == "Trip Wire Right")
            {
                obj.ImageID = tM.LoadTexture("Images/trip wire right.png");
                obj.Width = 16;
                obj.Height = 32;
            }
            else if (name == "Trip Wire Top")
            {
                obj.ImageID = tM.LoadTexture("Images/trip wire top.png");
                obj.Width = 32;
                obj.Height = 16;
            }
            else if (name == "Trip Wire Bottom")
            {
                obj.ImageID = tM.LoadTexture("Images/trip wire bottom.png");
                obj.Width = 32;
                obj.Height = 16;
            }
            else if (name == "Water")
            {
                obj.ImageID = tM.LoadTexture("Images/water.png");
                obj.Width = obj.Height = 64;
            }
            else if (name == "Water (Surface)")
            {
                obj.ImageID = tM.LoadTexture("Images/water surface.png");
                obj.Width = 64;
                obj.Height = 56;
            }
            else if (name == "Wood Box")
            {
                obj.ImageID = tM.LoadTexture("Images/wood box.png");
                obj.Width = obj.Height = 64;
            }

            gameObjects.Add(obj);
            UpdateObjectList();

            lb_Objects.SelectedIndex = gameObjects.Count - 1;
        }

        // Loads the specified object's settings into the editor.
        private void LoadObjectSettings(GameObject obj)
        {
            // Show the object settings.
            gb_ObjectSettings.Visible = true;
            gb_WaypointSettings.Visible = cb_UseWaypoints.Checked;

            // Set the object settings.
            nud_ObjectPosX.Value = obj.X;
            nud_ObjectPosY.Value = obj.Y;
            cb_UseWaypoints.Checked = obj.UseWaypoints;
            cb_UseEvent.Checked = obj.UseEvents;
            tb_ObjectTag.Text = obj.Tag;
            tb_UniqueData.Text = obj.Data;
            cb_WaypointMode.Text = obj.WaypointMode;
        }

        // Ubdates the list of gameobjects.
        private void UpdateObjectList()
        {
            lb_Objects.Items.Clear();

            for (int i = 0; i < gameObjects.Count; ++i)
                lb_Objects.Items.Add(gameObjects[i]);
        }

        // Returns the position of the center of the screen.
        private Point CenterScreenLocation()
        {
            return new Point(panel_Main.Width / 2 - tilemapOffset.X,
                panel_Main.Height / 2 - tilemapOffset.Y);
        }

        // Point in Rectangle check.
        private bool PointInRectangle(Point p, Rectangle r)
        {
            return (p.X >= r.X && p.X < r.Right && p.Y >= r.Y && p.Y < r.Bottom);
        }

        // Performs a flood fill algorithm on the tile set.
        private void FloodFillTiles(int tileX, int tileY)
        {
            // Create a list of points for tiles that are going to be filled.
            List<Point> fillTiles = new List<Point>();

            // Give it the starting tile.
            fillTiles.Add(new Point(tileX, tileY));

            // Save the tile that is to be replaced.
            Tile replaceTile = tilemap[tileX, tileY];

            // Don't paint if the replaceTile is the same as the selectedTile.
            if (replaceTile.Equals(selectedTile))
                return;

            // Loop through the current tiles.
            for (int i = 0; i < fillTiles.Count; ++i)
            {
                int x = fillTiles[i].X;
                int y = fillTiles[i].Y;

                // Add tiles above the current tile.
                for (bool loop = true; loop; )
                {
                    --y;

                    if (y >= 0)
                    {
                        if (tilemap[x, y].Same(replaceTile))
                            fillTiles.Add(new Point(x, y));
                        else
                            loop = false;
                    }
                    else
                        loop = false;
                }

                y = fillTiles[i].Y;

                // Add tiles below the current tile.
                for (bool loop = true; loop; )
                {
                    ++y;

                    if (y < mapHeight)
                    {
                        if (tilemap[x, y].Same(replaceTile))
                            fillTiles.Add(new Point(x, y));
                        else
                            loop = false;
                    }
                    else
                        loop = false;
                }

                y = fillTiles[i].Y;

                // Add tiles to the left of the current tile.
                for (bool loop = true; loop; )
                {
                    --x;

                    if (x >= 0)
                    {
                        if (tilemap[x, y].Same(replaceTile))
                            fillTiles.Add(new Point(x, y));
                        else
                            loop = false;
                    }
                    else
                        loop = false;
                }

                x = fillTiles[i].X;

                // Add tiles to the right of the current tile.
                for (bool loop = true; loop; )
                {
                    ++x;

                    if (x < mapWidth)
                    {
                        if (tilemap[x, y].Same(replaceTile))
                            fillTiles.Add(new Point(x, y));
                        else
                            loop = false;
                    }
                    else
                        loop = false;
                }

                // Set the current tile to the fill tile.
                tilemap[fillTiles[i].X, fillTiles[i].Y] = new Tile(selectedTile);

                // Remove the current tile from the list.
                fillTiles.RemoveAt(i);
                --i;
            }
        }

        // Selects a single tile with the wand tool.
        private void SelectSingleTile(int tileX, int tileY)
        {
            // Clear the existing selection.
            tileSelection.Clear();

            // Select the tile.
            tileSelection.Add(new Point(tileX, tileY));
        }

        // Selects all contiguous tiles of the same type.
        private void SelectContiguousTiles(int tileX, int tileY)
        {
            // Clear the existing selection.
            tileSelection.Clear();

            // Save the tile that is to be selected.
            Tile selectTile = tilemap[tileX, tileY];

            // Give it the starting tile.
            tileSelection.Add(new Point(tileX, tileY));

            // Loop through the current tiles.
            for (int i = 0; i < tileSelection.Count; ++i)
            {
                int x = tileSelection[i].X;
                int y = tileSelection[i].Y;

                // Add tiles above the current tile.
                for (bool loop = true; loop; )
                {
                    --y;

                    if (y >= 0)
                    {
                        if (tilemap[x, y].Same(selectTile))
                        {
                            bool canAdd = true;

                            for (int n = 0; n < tileSelection.Count; ++n)
                            {
                                if (tileSelection[n].X == x && tileSelection[n].Y == y)
                                {
                                    canAdd = false;
                                    break;
                                }
                            }

                            if(canAdd == true)
                                tileSelection.Add(new Point(x, y));
                        }
                        else
                            loop = false;
                    }
                    else
                        loop = false;
                }

                y = tileSelection[i].Y;

                // Add tiles below the current tile.
                for (bool loop = true; loop; )
                {
                    ++y;

                    if (y < mapHeight)
                    {
                        if (tilemap[x, y].Same(selectTile))
                        {
                            bool canAdd = true;

                            for (int n = 0; n < tileSelection.Count; ++n)
                            {
                                if (tileSelection[n].X == x && tileSelection[n].Y == y)
                                {
                                    canAdd = false;
                                    break;
                                }
                            }

                            if (canAdd == true)
                                tileSelection.Add(new Point(x, y));
                        }
                        else
                            loop = false;
                    }
                    else
                        loop = false;
                }

                y = tileSelection[i].Y;

                // Add tiles to the left of the current tile.
                for (bool loop = true; loop; )
                {
                    --x;

                    if (x >= 0)
                    {
                        if (tilemap[x, y].Same(selectTile))
                        {
                            bool canAdd = true;

                            for (int n = 0; n < tileSelection.Count; ++n)
                            {
                                if (tileSelection[n].X == x && tileSelection[n].Y == y)
                                {
                                    canAdd = false;
                                    break;
                                }
                            }

                            if (canAdd == true)
                                tileSelection.Add(new Point(x, y));
                        }
                        else
                            loop = false;
                    }
                    else
                        loop = false;
                }

                x = tileSelection[i].X;

                // Add tiles to the right of the current tile.
                for (bool loop = true; loop; )
                {
                    ++x;

                    if (x < mapWidth)
                    {
                        if (tilemap[x, y].Same(selectTile))
                        {
                            bool canAdd = true;

                            for (int n = 0; n < tileSelection.Count; ++n)
                            {
                                if (tileSelection[n].X == x && tileSelection[n].Y == y)
                                {
                                    canAdd = false;
                                    break;
                                }
                            }

                            if (canAdd == true)
                                tileSelection.Add(new Point(x, y));
                        }
                        else
                            loop = false;
                    }
                    else
                        loop = false;
                }
            }
        }

        // Selects all tiles on the map of the same type.
        private void SelectAllTiles(int tileX, int tileY)
        {
            // Clear the existing selection.
            tileSelection.Clear();

            // Save the tile that is to be selected.
            Tile selectTile = tilemap[tileX, tileY];

            // Loop through all tiles on the map.
            for (int x = 0; x < mapWidth; ++x)
            {
                for (int y = 0; y < mapHeight; ++y)
                {
                    if (tilemap[x, y].Same(selectTile))
                        tileSelection.Add(new Point(x, y));
                }
            }
        }

        // Centers the tile map to the screen.
        public void CenterTileMap()
        {
            // Get the size of the main panel.
            Size screenSize = panel_Main.Size;

            // Calculate the size of the tile map.
            Size mapSize = new Size(mapWidth * tileWidth, mapHeight * tileHeight);

            // Set the offset.
            tilemapOffset.X = (screenSize.Width / 2) - (mapSize.Width / 2);
            tilemapOffset.Y = (screenSize.Height / 2) - (mapSize.Height / 2);
        }

        // Load a tileset.
        private void btn_LoadTileset_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();

            if (DialogResult.OK == dlg.ShowDialog())
            {
                LoadTilesetImage(dlg.FileName);
            }
        }

        // Update the list of tilesets.
        private void UpdateTilesetList()
        {
            tilesets.Clear();

            for (int i = 0; i < lb_Tilesets.Items.Count; ++i)
            {
                tilesets.Add((Tileset)lb_Tilesets.Items[i]);
            }
        }

        // Update the list of collision boxes.
        private void UpdateCollisionBoxList()
        {
            lb_CollisionBoxes.Items.Clear();

            for (int i = 0; i < collisionBoxes.Count; ++i)
                lb_CollisionBoxes.Items.Add(collisionBoxes[i]);
        }

        // Update the list of waypoints.
        private void UpdateObjectWaypoints()
        {
            int waypointIndex = lb_Waypoints.SelectedIndex;
            int objectIndex = lb_Objects.SelectedIndex;

            GameObject obj = gameObjects[objectIndex];

            lb_Waypoints.Items.Clear();

            for (int i = 0; i < obj.Waypoints.Count; ++i)
                lb_Waypoints.Items.Add(obj.Waypoints[i]);

            if (waypointIndex >= lb_Waypoints.Items.Count)
                waypointIndex = gameObjects[objectIndex].Waypoints.Count - 1;

            lb_Waypoints.SelectedIndex = waypointIndex;
        }

        // Update the list of events.
        private void UpdateObjectEventList(GameObject obj)
        {
            lb_ObjectEvents.Items.Clear();

            for (int i = 0; i < obj.Events.Count; ++i)
            {
                lb_ObjectEvents.Items.Add(obj.Events[i]);
            }
        }

        // Updates the list of event tags.
        private void UpdateEventTagList(Event e)
        {
            lb_EventObjectTags.Items.Clear();

            for (int i = 0; i < e.Tags.Count; ++i)
            {
                lb_EventObjectTags.Items.Add(e.Tags[i]);
            }
        }

        // Loads the waypoint settings into the editor.
        private void LoadWaypointSettings(GameObject obj)
        {

        }

        // Selected tileset index changes.
        private void lb_Tilesets_SelectedIndexChanged(object sender, EventArgs e)
        {
            // No tileset selected.
            if (lb_Tilesets.SelectedIndex == -1)
            {
                btn_RemoveTileset.Enabled = false;
                //btn_RenameTileset.Enabled = false;
                selectedTile.TilesetID = -1;
            }
            else
            {
                btn_RemoveTileset.Enabled = true;
                //btn_RenameTileset.Enabled = true;
                selectedTile.TilesetID = lb_Tilesets.SelectedIndex;
            }
        }

        // The active tab has changed.
        private void tabControl_Main_SelectedIndexChanged(object sender, EventArgs e)
        {
            string tabName = tabControl_Main.SelectedTab.Text;

            if (tabName == "Map")
            {
                // Hide the tool strip paint and erases buttons.
                ts_btn_Pencil.Visible = true;
                ts_btn_Eraser.Visible = true;
                ts_btn_TileSelect.Visible = true;
                ts_btn_CollisionBoxes.Visible = false;
                ts_btn_ObjectPlacement.Visible = false;
                ts_btn_WaypointPlacement.Visible = false;
                ts_btn_Paint.Visible = false;
                ts_btn_Wand.Visible = false;

                tileSelection.Clear();

                tool = Tool.TILE_PENCIL;
            }
            else if (tabName == "Tiles")
            {
                // Show the tool strip paint and erases buttons.
                ts_btn_Pencil.Visible = true;
                ts_btn_Eraser.Visible = true;
                ts_btn_TileSelect.Visible = true;
                ts_btn_CollisionBoxes.Visible = false;
                ts_btn_ObjectPlacement.Visible = false;
                ts_btn_WaypointPlacement.Visible = false;
                ts_btn_Paint.Visible = true;
                ts_btn_Wand.Visible = true;

                tileSelection.Clear();

                tool = Tool.TILE_PENCIL;
            }
            else if (tabName == "Objects")
            {
                // Hide the tool strip paint and erases buttons.
                ts_btn_Pencil.Visible = false;
                ts_btn_Eraser.Visible = false;
                ts_btn_TileSelect.Visible = false;
                ts_btn_CollisionBoxes.Visible = false;
                ts_btn_ObjectPlacement.Visible = true;
                ts_btn_WaypointPlacement.Visible = true;
                ts_btn_Paint.Visible = false;
                ts_btn_ObjectPlacement.Checked = true;
                ts_btn_WaypointPlacement.Checked = false;
                ts_btn_Wand.Visible = false;

                tileSelection.Clear();

                // Set the current edit mode.
                tool = Tool.OBJECT_SELECT;
            }
            else if (tabName == "Collision")
            {
                // Hide the tool strip paint and erases buttons.
                ts_btn_Pencil.Visible = false;
                ts_btn_Eraser.Visible = false;
                ts_btn_TileSelect.Visible = false;
                ts_btn_CollisionBoxes.Visible = true;
                ts_btn_ObjectPlacement.Visible = false;
                ts_btn_WaypointPlacement.Visible = false;
                ts_btn_Paint.Visible = false;
                ts_btn_Wand.Visible = false;

                tileSelection.Clear();

                tool = Tool.COLLISION_DRAW;
            }
        }

        // The pencil tool button was clicked.
        private void ts_btn_Pencil_Click(object sender, EventArgs e)
        {
            ts_btn_Pencil.Checked = true;
            ts_btn_Eraser.Checked = false;
            ts_btn_TileSelect.Checked = false;
            ts_btn_Paint.Checked = false;
            ts_btn_Wand.Checked = false;

            ts_Sep_Wand.Visible = false;
            ts_cb_WandSetting.Visible = false;

            // Set the current tool to the tile painter.
            tool = Tool.TILE_PENCIL;

            tileSelection.Clear();
        }

        // Activate the paint tile fill tool.
        private void ts_btn_Paint_Click(object sender, EventArgs e)
        {
            ts_btn_Pencil.Checked = false;
            ts_btn_Eraser.Checked = false;
            ts_btn_TileSelect.Checked = false;
            ts_btn_Paint.Checked = true;
            ts_btn_Wand.Checked = false;

            ts_Sep_Wand.Visible = false;
            ts_cb_WandSetting.Visible = false;

            tool = Tool.TILE_PAINT;

            tileSelection.Clear();
        }

        // Activate the wand select tool.
        private void ts_btn_Wand_Click(object sender, EventArgs e)
        {
            ts_btn_Eraser.Checked = false;
            ts_btn_Pencil.Checked = false;
            ts_btn_TileSelect.Checked = false;
            ts_btn_Paint.Checked = false;
            ts_btn_Wand.Checked = true;

            ts_Sep_Wand.Visible = true;
            ts_cb_WandSetting.Visible = true;

            // Set the current tool to the tile eraser.
            tool = Tool.TILE_WAND;
        }

        // Change the current tool to the tile select mode.
        private void ts_btn_TileSelect_Click(object sender, EventArgs e)
        {
            ts_btn_Pencil.Checked = false;
            ts_btn_Eraser.Checked = false;
            ts_btn_TileSelect.Checked = true;
            ts_btn_Paint.Checked = false;
            ts_btn_Wand.Checked = false;

            ts_Sep_Wand.Visible = false;
            ts_cb_WandSetting.Visible = false;

            tool = Tool.TILE_SELECT;

            tileSelection.Clear();
        }

        // The eraser tool button was clicked.
        private void ts_btn_Eraser_Click(object sender, EventArgs e)
        {
            ts_btn_Eraser.Checked = true;
            ts_btn_Pencil.Checked = false;
            ts_btn_TileSelect.Checked = false;
            ts_btn_Paint.Checked = false;
            ts_btn_Wand.Checked = false;

            ts_Sep_Wand.Visible = false;
            ts_cb_WandSetting.Visible = false;

            // Set the current tool to the tile eraser.
            tool = Tool.TILE_ERASE;

            tileSelection.Clear();
        }

        // Mouse was clicked in the tileset preview window.
        // - Selection of a tile is performed here.
        private void panel_Tileset_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                // Is there an active tileset?
                if (lb_Tilesets.SelectedIndex != -1)
                {
                    // Calculate the selection position.
                    Point selectPos = e.Location;
                    selectPos.X -= tilesetOffset.X;
                    selectPos.Y -= tilesetOffset.Y;

                    // Get the selected tile set.
                    Tileset tileset = tilesets[lb_Tilesets.SelectedIndex];

                    int tileX = selectPos.X / tileWidth;
                    int tileY = selectPos.Y / tileHeight;

                    if (tileX >= 0 && tileX < tileset.Columns)
                    {
                        if (tileY >= 0 && tileY < tileset.Rows)
                        {
                            // Set the selected tile's values.
                            selectedTile.TilesetID = tilesets[lb_Tilesets.SelectedIndex].ImageID;
                            selectedTile.PosX = tileX;
                            selectedTile.PosY = tileY;
                        }
                    }
                }
            }
        }

        // Mouse was CLICKED in the TILEMAP preview window.
        private void panel_Main_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                if (tool == Tool.COLLISION_DRAW)
                {
                    // Loop through all collision boxes.
                    for (int i = 0; i < collisionBoxes.Count; ++i)
                    {
                        Point p = new Point(e.Location.X - tilemapOffset.X, e.Location.Y - tilemapOffset.Y);

                        if (PointInRectangle(p, collisionBoxes[i].Bounds()))
                        {
                            lb_CollisionBoxes.SelectedIndex = i;
                            break;
                        }
                    }
                }
            }
        }

        // Center map menu tool strip button clicked.
        private void centerMapToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CenterTileMap();
        }

        // Mouse button is held DOWN in the TILEMAP panel.
        private void panel_Main_MouseDown(object sender, MouseEventArgs e)
        {
            // Get the position of the mouse click.
            previousMousePosition = e.Location;

            // Right mouse button is pressed.
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                if (!rMouseDown)
                {
                    rMouseDown = true;
                }
            }
            // Left mouse button is pressed.
            else if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if (!lMouseDown)
                {
                    lMouseDown = true;

                    // Should we be painting collision boxes?
                    if (tool == Tool.COLLISION_DRAW)
                    {
                        // Set the start position of the collision selection box.
                        Point p = new Point(e.Location.X - tilemapOffset.X, e.Location.Y - tilemapOffset.Y);
                        collisionSelection.Start = p;

                        // Set the end position of the collision selection box.
                        p = new Point(e.Location.X - tilemapOffset.X, e.Location.Y - tilemapOffset.Y);
                        collisionSelection.End = p;

                        // Enable the collision selection.'
                        collisionSelection.Enabled = true;
                    }
                    // Select/deselect objects.
                    else if (tool == Tool.OBJECT_SELECT)
                    {
                        // Loop through all game objects.
                        bool clickedOnObject = false;
                        for (int i = 0; i < gameObjects.Count; ++i)
                        {
                            // Did the mouse click on one?
                            Point p = e.Location;
                            p.X -= tilemapOffset.X;
                            p.Y -= tilemapOffset.Y;

                            if(PointInRectangle(p, gameObjects[i].Bounds))
                            {
                                lb_Objects.SelectedIndex = i;
                                clickedOnObject = true;
                                UpdateObjectWaypoints();
                                break;
                            }
                        }

                        // Deselect any currently selected object.
                        if (clickedOnObject == false)
                            lb_Objects.SelectedIndex = -1;
                        else
                            UpdateObjectWaypoints();
                    }
                    else if (tool == Tool.WAYPOINT_SELECT)
                    {
                        // Is an object selected?
                        if (lb_Objects.SelectedIndex != -1)
                        {
                            int index = lb_Objects.SelectedIndex;

                            // Does the object support waypoints?
                            if (gameObjects[index].UseWaypoints == true)
                            {
                                bool wasWaypointSelected = (lb_Waypoints.SelectedIndex >= 0);
                                bool clickedOnWaypoint = false;
                                // Loop through the selected object's waypoints.
                                for (int i = 0; i < gameObjects[index].Waypoints.Count; ++i)
                                {
                                    // Get the current waypoint.
                                    Point waypoint = gameObjects[index].Waypoints[i];

                                    // Create a selection box around the waypoint.
                                    Rectangle box = new Rectangle();
                                    box.X = waypoint.X - 6;
                                    box.Y = waypoint.Y - 6;
                                    box.Width = 12;
                                    box.Height = 12;

                                    Point p = e.Location;
                                    p.X -= tilemapOffset.X;
                                    p.Y -= tilemapOffset.Y;

                                    // Check to see if the mouse clicked the waypoint.
                                    if (PointInRectangle(p, box) == true)
                                    {
                                        // Store the selected waypoint index.
                                        gameObjects[index].SelectedWaypoint = i;
                                        lb_Waypoints.SelectedIndex = i;
                                        clickedOnWaypoint = true;
                                        break;
                                    }
                                }

                                // If a waypoint was selected, load the waypoint's settings.
                                if (clickedOnWaypoint == true)
                                {
                                    LoadWaypointSettings(gameObjects[index]);
                                }
                                else // Since the object supports waypoints, if no waypoint was selected,
                                {    // create a new waypoint.
                                    if (wasWaypointSelected == false)
                                    {
                                        Point p = e.Location;
                                        p.X -= tilemapOffset.X;
                                        p.Y -= tilemapOffset.Y;
                                        gameObjects[index].Waypoints.Add(p);
                                        UpdateObjectWaypoints();
                                    }
                                    else
                                    {
                                        gameObjects[index].SelectedWaypoint = -1;
                                        lb_Waypoints.SelectedIndex = -1;
                                        UpdateObjectWaypoints();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        // Mouse button is RELEASED in the TILEMAP panel
        private void panel_Main_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
                rMouseDown = false;
            else if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                lMouseDown = false;
                
                if (tool == Tool.TILE_ERASE)
                    RemoveTile(e);
                else if(tool == Tool.TILE_PENCIL)
                    PlaceTile(e);
                else if (tool == Tool.TILE_PAINT)
                {
                    // Is there a tile selected?
                    if (selectedTile.TilesetID != -1)
                    {
                        // Get the coordinates of the tile selected.
                        int tileX = (e.X - tilemapOffset.X) / tileWidth;
                        int tileY = (e.Y - tilemapOffset.Y) / tileHeight;

                        // Flood fill the tiles!
                        FloodFillTiles(tileX, tileY);
                    }
                }
                else if (tool == Tool.TILE_WAND)
                {
                    // Get the coordinates of the tile selected.
                    int tileX = (e.X - tilemapOffset.X) / tileWidth;
                    int tileY = (e.Y - tilemapOffset.Y) / tileHeight;

                    if (ts_cb_WandSetting.SelectedIndex == 0)
                        SelectSingleTile(tileX, tileY);
                    else if (ts_cb_WandSetting.SelectedIndex == 1)
                        SelectContiguousTiles(tileX, tileY);
                    else if (ts_cb_WandSetting.SelectedIndex == 2)
                        SelectAllTiles(tileX, tileY);
                }
                else if (tool == Tool.COLLISION_DRAW)
                {
                    // Store the selected index.
                    int index = lb_CollisionBoxes.SelectedIndex;

                    // Create a new collision box if no collision box is already selected.
                    if (index == -1)
                    {
                        if (collisionSelection.Width() > 0 && collisionSelection.Height() > 0)
                        {
                            CollisionBox box = new CollisionBox();

                            box.X = collisionSelection.Start.X;
                            box.Y = collisionSelection.Start.Y;
                            box.Width = collisionSelection.Width();
                            box.Height = collisionSelection.Height();
                            box.Name = "Collision Box " + collisionBoxes.Count.ToString();

                            collisionBoxes.Add(box);

                            UpdateCollisionBoxList();

                            lb_CollisionBoxes.SelectedIndex = index;
                        }
                    }
                    else
                    {
                        CollisionBox box = collisionBoxes[lb_CollisionBoxes.SelectedIndex];

                        if (collisionSelection.Width() > 0 && collisionSelection.Height() > 0)
                        {
                            box.X = collisionSelection.Start.X;
                            box.Y = collisionSelection.Start.Y;
                            box.Width = collisionSelection.Width();
                            box.Height = collisionSelection.Height();

                            collisionBoxes[lb_CollisionBoxes.SelectedIndex] = box;

                            UpdateCollisionBoxList();

                            lb_CollisionBoxes.SelectedIndex = index;
                        }
                    }

                    collisionSelection.Enabled = false;
                }
                else if (tool == Tool.OBJECT_SELECT)
                {
                    int index = lb_Objects.SelectedIndex;

                    if (index != -1)
                    {
                        if (snapObjectsToGrid == true)
                        {
                            gameObjects[index].X -= gameObjects[index].X % tileWidth;
                            gameObjects[index].Y -= gameObjects[index].Y % tileHeight;

                            nud_ObjectPosX.Value = gameObjects[index].X;
                            nud_ObjectPosY.Value = gameObjects[index].Y;
                        }
                    }
                }
            }
            else if (e.Button == System.Windows.Forms.MouseButtons.Middle)
                CenterTileMap();
        }

        // Mouse button is MOVING in TILEMAP panel.
        private void panel_Main_MouseMove(object sender, MouseEventArgs e)
        {
            // Store the current position of the mouse in the tilemap window.
            tilemapMousePosition = e.Location;

            if (rMouseDown == true)
            {
                tilemapOffset.X += e.X - previousMousePosition.X;
                tilemapOffset.Y += e.Y - previousMousePosition.Y;
            }
            else if (lMouseDown == true)
            {
                if (tool == Tool.TILE_ERASE)
                    RemoveTile(e);
                else if (tool == Tool.TILE_PENCIL)
                    PlaceTile(e);
                else if (tool == Tool.COLLISION_DRAW)
                {
                    // Set the end position of the collision selection box.
                    Point p = new Point(e.Location.X - tilemapOffset.X, e.Location.Y - tilemapOffset.Y);
                    collisionSelection.End = p;
                }
                else if (tool == Tool.OBJECT_SELECT)
                {
                    // Is there an object selected?
                    if (lb_Objects.SelectedIndex != -1)
                    {
                        int index = lb_Objects.SelectedIndex;

                        Point p = previousMousePosition;
                        p.X -= tilemapOffset.X;
                        p.Y -= tilemapOffset.Y;

                        if(PointInRectangle(p, gameObjects[index].Bounds))
                        {
                            gameObjects[index].X += e.X - previousMousePosition.X;
                            gameObjects[index].Y += e.Y - previousMousePosition.Y;

                            nud_ObjectPosX.Value = gameObjects[index].X;
                            nud_ObjectPosY.Value = gameObjects[index].Y;
                        }
                    }
                }
                else if (tool == Tool.WAYPOINT_SELECT)
                {
                    // Is an object selected?
                    if (lb_Objects.SelectedIndex != -1)
                    {
                        int index = lb_Objects.SelectedIndex;

                        // Is there a waypoint selected?
                        
                        if (gameObjects[index].SelectedWaypoint != -1)
                        {
                            Point waypoint = gameObjects[index].Waypoints[gameObjects[index].SelectedWaypoint];

                            // Create a selection box around the waypoint.
                            Rectangle box = new Rectangle();
                            box.X = waypoint.X - 6;
                            box.Y = waypoint.Y - 6;
                            box.Width = 12;
                            box.Height = 12;

                            Point p = previousMousePosition;
                            p.X -= tilemapOffset.X;
                            p.Y -= tilemapOffset.Y;

                            // Check to see if the mouse is dragging the waypoint.
                            if (PointInRectangle(p, box) == true)
                            {
                                waypoint.X += e.Location.X - previousMousePosition.X;
                                waypoint.Y += e.Location.Y - previousMousePosition.Y;
                                gameObjects[index].Waypoints[gameObjects[index].SelectedWaypoint] = waypoint;

                                UpdateObjectWaypoints();
                            }
                        }
                    }
                }
            }

            // Show mouse position in status strip.
            string mousePosition = "Mouse: (" + (e.X + tilemapOffset.X).ToString() + ", " + (e.Y + tilemapOffset.Y).ToString() + ")";
            statusStrip_Main.Items[0].Text = mousePosition;

            previousMousePosition.X = e.X;
            previousMousePosition.Y = e.Y;
        }

        // Mouse button is held DOWN in the TILESET panel.
        private void panel_Tileset_MouseDown(object sender, MouseEventArgs e)
        {
            // Right mouse button is pressed.
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                if (!rMouseDown)
                {
                    rMouseDown = true;

                    // Get the position of the mouse click.
                    previousMousePosition = e.Location;
                }
            }
            // Left mouse button is pressed.
            else if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if (!lMouseDown)
                {
                    lMouseDown = true;
                }
            }
        }

        // Mouse button is RELEASED in the TILESET panel.
        private void panel_Tileset_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
                rMouseDown = false;
            else if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                lMouseDown = false;

                if (tool == Tool.TILE_WAND)
                {
                    for (int i = 0; i < tileSelection.Count; ++i)
                        tilemap[tileSelection[i].X, tileSelection[i].Y] = new Tile(selectedTile);
                }
            }
            else if (e.Button == System.Windows.Forms.MouseButtons.Middle)
            {
                tilesetOffset.X = 0;
                tilesetOffset.Y = 0;
            }
        }

        // Mouse button is MOVING in TILESET panel.
        private void panel_Tileset_MouseMove(object sender, MouseEventArgs e)
        {
            if (rMouseDown == true)
            {
                tilesetOffset.X += e.X - previousMousePosition.X;
                tilesetOffset.Y += e.Y - previousMousePosition.Y;

                previousMousePosition.X = e.X;
                previousMousePosition.Y = e.Y;
            }
        }

        // Toggle grid rendering.
        private void gridToolStripMenuItem_Click(object sender, EventArgs e)
        {
            drawGrid = gridToolStripMenuItem.Checked = !gridToolStripMenuItem.Checked;
        }

        // Toggle object rendering.
        private void objectsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            drawObjects = objectsToolStripMenuItem.Checked = !objectsToolStripMenuItem.Checked;
        }

        // Toggle waypoint rendering.
        private void waypointsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            drawWaypoints = waypointsToolStripMenuItem.Checked = !waypointsToolStripMenuItem.Checked;
        }

        // Toggle collision box rendering.
        private void collisionBoxesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            drawCollisionBoxes = collisionBoxesToolStripMenuItem.Checked = !collisionBoxesToolStripMenuItem.Checked;
        }

        // Make the currently selected tile the default tile.
        private void makeSelectedDefaultToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Is there a tile selected?
            if (selectedTile.TilesetID != -1)
            {
                defaultTile.TilesetID = selectedTile.TilesetID;
                defaultTile.PosX = selectedTile.PosX;
                defaultTile.PosY = selectedTile.PosY;
            }
        }

        // Remove the default tile.
        private void toolStripMenuItem3_Click(object sender, EventArgs e)
        {
            defaultTile.TilesetID = -1;
            defaultTile.PosX = 0;
            defaultTile.PosY = 0;
        }

        // Clears the entire map.
        private void clearMapToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show("Are you sure you want to clear the map?\nThis opperation cannot be undone.",
                "Are you sure?", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);

            if (result == System.Windows.Forms.DialogResult.Yes)
            {
                ClearMap();
            }
        }

        // Change the tile width.
        private void nud_TileWidth_ValueChanged(object sender, EventArgs e)
        {
            tileWidth = (int)nud_TileWidth.Value;
            
            nud_SpawnPosXL.Increment = tileWidth;

            // Center the map again.
            CenterTileMap();
        }

        // Change the tile height.
        private void nud_TileHeight_ValueChanged(object sender, EventArgs e)
        {
            tileHeight = (int)nud_TileHeight.Value;

            nud_SpawnPosYL.Increment = tileHeight;

            // Center the map again.
            CenterTileMap();
        }

        // Set the tile width to a preset value.
        private void cb_Tile_PresetWidth_SelectedIndexChanged(object sender, EventArgs e)
        {
            nud_TileWidth.Value = Convert.ToDecimal(cb_Tile_PresetWidth.Text);
        }

        // Set the tile height to a preset value.
        private void cb_Tile_PresetHeight_SelectedIndexChanged(object sender, EventArgs e)
        {
            nud_TileHeight.Value = Convert.ToDecimal(cb_Tile_PresetHeight.Text);
        }

        // Map width has changed.
        private void nud_MapWidth_ValueChanged(object sender, EventArgs e)
        {
            MapSizeChanged();
        }

        // Map height has changed.
        private void nud_MapHeight_ValueChanged(object sender, EventArgs e)
        {
            MapSizeChanged();
        }

        // Handles the map size alterations.
        private void MapSizeChanged()
        {
            newMapWidth = (int)nud_MapWidth.Value;
            newMapHeight = (int)nud_MapHeight.Value;

            // Has the map size changed already?
            if (mapSizeChanged == false)
            {
                mapSizeChanged = true;

                btn_Apply.Enabled = true;
                btn_Cancel.Enabled = true;

                lbl_MapSettings_ShiftLeft.Visible = true;
                lbl_MapSettings_ShiftUp.Visible = true;
                nud_ShiftLeft.Visible = true;
                nud_ShiftUp.Visible = true;
                nud_ShiftLeft.Enabled = true;
                nud_ShiftUp.Enabled = true;
            }
            else
            {
                // Did they reset the map size?
                if (mapWidth == newMapWidth && mapHeight == newMapHeight)
                {
                    mapSizeChanged = false;

                    btn_Apply.Enabled = false;
                    btn_Cancel.Enabled = false;

                    lbl_MapSettings_ShiftLeft.Visible = false;
                    lbl_MapSettings_ShiftUp.Visible = false;
                    nud_ShiftLeft.Visible = false;
                    nud_ShiftUp.Visible = false;
                    nud_ShiftLeft.Enabled = false;
                    nud_ShiftUp.Enabled = false;
                }
            }
        }

        // Apply the new map size settings.
        private void btn_Apply_Click(object sender, EventArgs e)
        {
            ResizeMap(newMapWidth, newMapHeight);

            // Shift all collision boxes.
            for (int i = 0; i < collisionBoxes.Count; ++i)
            {
                collisionBoxes[i].X += (int)nud_ShiftLeft.Value * tileWidth;
                collisionBoxes[i].Y += (int)nud_ShiftUp.Value * tileHeight;

                nud_CollisionBoxPositionX.Value = collisionBoxes[i].X;
                nud_CollisionBoxPositionY.Value = collisionBoxes[i].Y;
            }

            // Shift all objects.
            for (int i = 0; i < gameObjects.Count; ++i)
            {
                gameObjects[i].X += (int)nud_ShiftLeft.Value * tileWidth;
                gameObjects[i].Y += (int)nud_ShiftUp.Value * tileHeight;

                nud_ObjectPosX.Value = gameObjects[i].X;
                nud_ObjectPosY.Value = gameObjects[i].Y;
            }

            UpdateCollisionBoxList();

            mapWidth = newMapWidth;
            mapHeight = newMapHeight;
            nud_MapWidth.Value = mapWidth;
            nud_MapHeight.Value = mapHeight;

            nud_ShiftLeft.Value = 0;
            nud_ShiftUp.Value = 0;

            mapSizeChanged = false;

            btn_Apply.Enabled = false;
            btn_Cancel.Enabled = false;

            lbl_MapSettings_ShiftLeft.Visible = false;
            lbl_MapSettings_ShiftUp.Visible = false;
            nud_ShiftLeft.Visible = false;
            nud_ShiftUp.Visible = false;
            nud_ShiftLeft.Enabled = false;
            nud_ShiftUp.Enabled = false;

            CenterTileMap();
        }

        // Disregard the new map size settings.
        private void btn_Cancel_Click(object sender, EventArgs e)
        {
            newMapWidth = mapWidth;
            newMapHeight = mapHeight;
            nud_MapWidth.Value = mapWidth;
            nud_MapHeight.Value = mapHeight;

            nud_ShiftLeft.Value = 0;
            nud_ShiftUp.Value = 0;
        }

        // Resize the render target for the tileset.
        private void panel_Tileset_Resize(object sender, EventArgs e)
        {
            if(d3d != null)
                d3d.Resize(panel_Tileset, true);
        }

        // Resize the render target for the tilemap.
        private void panel_Main_Resize(object sender, EventArgs e)
        {
            if (d3d != null)
                d3d.Resize(panel_Main, true);
        }

        // Attempt to remove the tileset from the list.
        private void btn_RemoveTileset_Click(object sender, EventArgs e)
        {
            int searchIndex = tilesets[lb_Tilesets.SelectedIndex].ImageID;
            bool foundMatch = false;

            for(int x = 0; x < mapWidth; ++x)
            {
                for(int y = 0; y < mapHeight; ++y)
                {
                    if (tilemap[x, y].TilesetID == searchIndex)
                    {
                        foundMatch = true;
                        break;
                    }
                }
                if (foundMatch == true)
                    break;
            }

            if (defaultTile.TilesetID == searchIndex)
                foundMatch = true;

            if (foundMatch == true)
            {
                MessageBox.Show("The selected tilemap is currently in use!", "Unable to complete operation.",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                // Remove the tileset
                lb_Tilesets.Items.RemoveAt(searchIndex);

                tM.UnloadTexture(searchIndex);

                // Update the tileset list.
                UpdateTilesetList();
            }
        }

        // The collision box list box selected index has been changed.
        private void lb_CollisionBoxes_SelectedIndexChanged(object sender, EventArgs e)
        {
            int index = lb_CollisionBoxes.SelectedIndex;
            if (index == -1)
            {
                btn_DeselectCollisionBox.Enabled = false;
                btn_RemoveCollisionBox.Enabled = false;
                tb_CollisionBoxName.Enabled = false;

                gb_CollisionBoxSettings.Visible = false;
                gb_Collision_GridSettings.Visible = false;
            }
            else
            {
                btn_DeselectCollisionBox.Enabled = true;
                btn_RemoveCollisionBox.Enabled = true;
                tb_CollisionBoxName.Enabled = true;

                gb_CollisionBoxSettings.Visible = true;
                gb_Collision_GridSettings.Visible = true;

                // Get the selected box.
                CollisionBox box = collisionBoxes[index];

                // Load its settings to the editor.
                nud_CollisionBoxPositionX.Value = box.X;
                nud_CollisionBoxPositionY.Value = box.Y;
                nud_CollisionBoxWidth.Value = box.Width;
                nud_CollisionBoxHeight.Value = box.Height;
                tb_CollisionBoxTag.Text = box.Tag;
                tb_CollisionBoxName.Text = box.Name;
                cb_CanBeGrappled.Checked = box.CanGrappleAttach;
            }
        }

        // Deselect any collision box in the list.
        private void btn_DeselectCollisionBox_Click(object sender, EventArgs e)
        {
            lb_CollisionBoxes.SelectedIndex = -1;
        }

        // Change the x position of the selected collision box.
        private void nud_CollisionBoxPositionX_ValueChanged(object sender, EventArgs e)
        {
            int index = lb_CollisionBoxes.SelectedIndex;

            if (index != -1)
            {
                CollisionBox selectedBox = collisionBoxes[index];

                selectedBox.X = (int)nud_CollisionBoxPositionX.Value;

                collisionBoxes[index] = selectedBox;

                UpdateCollisionBoxList();

                lb_CollisionBoxes.SelectedIndex = index;
            }
        }

        // Change the y position of the selected collision box.
        private void nud_CollisionBoxPositionY_ValueChanged(object sender, EventArgs e)
        {
            int index = lb_CollisionBoxes.SelectedIndex;

            if (index != -1)
            {
                CollisionBox selectedBox = collisionBoxes[index];

                selectedBox.Y = (int)nud_CollisionBoxPositionY.Value;

                collisionBoxes[index] = selectedBox;

                UpdateCollisionBoxList();

                lb_CollisionBoxes.SelectedIndex = index;
            }
        }

        // Change the width of the selected collision box.
        private void nud_CollisionBoxWidth_ValueChanged(object sender, EventArgs e)
        {
            int index = lb_CollisionBoxes.SelectedIndex;

            if (index != -1)
            {
                CollisionBox selectedBox = collisionBoxes[index];

                if ((int)nud_CollisionBoxWidth.Value < collisionGridWidth)
                    if (snapCollisionBoxesToGrid == true)
                        nud_CollisionBoxWidth.Value = collisionGridWidth;

                selectedBox.Width = (int)nud_CollisionBoxWidth.Value;

                collisionBoxes[index] = selectedBox;

                UpdateCollisionBoxList();

                lb_CollisionBoxes.SelectedIndex = index;
            }
        }

        // Change the height of the selected collision box.
        private void nud_CollisionBoxHeight_ValueChanged(object sender, EventArgs e)
        {
            int index = lb_CollisionBoxes.SelectedIndex;

            if (index != -1)
            {
                CollisionBox selectedBox = collisionBoxes[index];

                if ((int)nud_CollisionBoxHeight.Value < collisionGridHeight)
                    if (snapCollisionBoxesToGrid == true)
                        nud_CollisionBoxHeight.Value = collisionGridHeight;

                selectedBox.Height = (int)nud_CollisionBoxHeight.Value;

                collisionBoxes[index] = selectedBox;

                UpdateCollisionBoxList();

                lb_CollisionBoxes.SelectedIndex = index;
            }
        }

        // Remove the selected collision box from the list.
        private void btn_RemoveCollisionBox_Click(object sender, EventArgs e)
        {
            int index = lb_CollisionBoxes.SelectedIndex;

            collisionBoxes.RemoveAt(index);

            UpdateCollisionBoxList();

            if (collisionBoxes.Count == 0)
            {
                btn_DeselectCollisionBox.Enabled = false;
                btn_RemoveCollisionBox.Enabled = false;
                tb_CollisionBoxName.Enabled = false;
                gb_CollisionBoxSettings.Visible = false;
            }
            else if (index < collisionBoxes.Count)
                lb_CollisionBoxes.SelectedIndex = index;
            else if (index - 1 < collisionBoxes.Count)
                lb_CollisionBoxes.SelectedIndex = index - 1;
        }

        // Change the tag of the selected collision box.
        private void tb_CollisionBoxTag_TextChanged(object sender, EventArgs e)
        {
            int index = lb_CollisionBoxes.SelectedIndex;

            CollisionBox selectedBox = collisionBoxes[index];

            selectedBox.Tag = tb_CollisionBoxTag.Text;

            collisionBoxes[index] = selectedBox;
        }

        // Toggle whether the selected collision box can be grappled.
        private void cb_CanBeGrappled_CheckedChanged(object sender, EventArgs e)
        {
            int index = lb_CollisionBoxes.SelectedIndex;

            CollisionBox selectedBox = collisionBoxes[index];

            selectedBox.CanGrappleAttach = cb_CanBeGrappled.Checked;

            collisionBoxes[index] = selectedBox;
        }

        // Change the name of the selected collision box.
        private void tb_CollisionBoxName_TextChanged(object sender, EventArgs e)
        {
            int index = lb_CollisionBoxes.SelectedIndex;

            collisionBoxes[index].Name = tb_CollisionBoxName.Text;

            UpdateCollisionBoxList();

            lb_CollisionBoxes.SelectedIndex = index;
        }

        // Export the level data.
        private void exportToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveLevel();
        }

        // Save the puzzle.
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SavePuzzle();
        }

        // Save the puzzle.
        private void ts_btn_Save_Click(object sender, EventArgs e)
        {
            SavePuzzle();
        }

        // Open a puzzle
        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenPuzzle();
        }

        // Open a puzzle
        private void ts_btn_Open_Click(object sender, EventArgs e)
        {
            OpenPuzzle();
        }

        // Select an object type from the drop-down menu.
        private void cb_Objects_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cb_Objects.SelectedIndex != -1)
            {
                btn_AddObject.Enabled = true;
            }
            else
            {
                btn_AddObject.Enabled = false;
            }
        }

        // Add the choosen object to the screen.
        private void btn_AddObject_Click(object sender, EventArgs e)
        {
            CreateObject(cb_Objects.Text);
        }

        // Change the selected object's x position.
        private void nud_ObjectPosX_ValueChanged(object sender, EventArgs e)
        {
            int index = lb_Objects.SelectedIndex;

            if (index != -1)
                gameObjects[index].X = (int)nud_ObjectPosX.Value;
        }

        // Change the selected object's y position.
        private void nud_ObjectPosY_ValueChanged(object sender, EventArgs e)
        {
            int index = lb_Objects.SelectedIndex;

            if (index != -1)
                gameObjects[index].Y = (int)nud_ObjectPosY.Value;
        }

        // The selected object index changed.
        private void lb_Objects_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lb_Objects.SelectedIndex != -1)
            {
                LoadObjectSettings(gameObjects[lb_Objects.SelectedIndex]);
                UpdateObjectEventList(gameObjects[lb_Objects.SelectedIndex]);
                btn_RemoveObject.Enabled = true;
            }
            else
            {
                gb_ObjectSettings.Visible = false;
                gb_WaypointSettings.Visible = false;
                gb_ObjectEvent.Visible = false;
                btn_RemoveObject.Enabled = false;
            }
        }

        // Edit the selected object's tag.
        private void tb_ObjectTag_TextChanged(object sender, EventArgs e)
        {
            int index = lb_Objects.SelectedIndex;

            gameObjects[index].Tag = tb_ObjectTag.Text;

            UpdateObjectList();

            lb_Objects.SelectedIndex = index;
        }

        // Toggle the use of object waypoints.
        private void cb_UseWaypoints_CheckedChanged(object sender, EventArgs e)
        {
            if (lb_Objects.SelectedIndex != -1)
            {
                gb_WaypointSettings.Visible = cb_UseWaypoints.Checked;
                gameObjects[lb_Objects.SelectedIndex].UseWaypoints = cb_UseWaypoints.Checked;

                UpdateObjectWaypoints();
            }
        }

        // Object placement tool was selected.
        private void ts_btn_ObjectPlacement_Click(object sender, EventArgs e)
        {
            ts_btn_ObjectPlacement.Checked = true;
            ts_btn_WaypointPlacement.Checked = false;
            tool = Tool.OBJECT_SELECT;
        }

        // Waypoint placement tool was selected.
        private void ts_btn_WaypointPlacement_Click(object sender, EventArgs e)
        {
            ts_btn_ObjectPlacement.Checked = false;
            ts_btn_WaypointPlacement.Checked = true;
            tool = Tool.WAYPOINT_SELECT;
        }

        // A waypoint was selected.
        private void lb_Waypoints_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lb_Waypoints.SelectedIndex != -1)
            {
                btn_RemoveNode.Enabled = true;
                btn_MoveNodeUp.Enabled = true;
                btn_MoveNodeDown.Enabled = true;
            }
            else
            {
                btn_RemoveNode.Enabled = false;
                btn_MoveNodeUp.Enabled = false;
                btn_MoveNodeDown.Enabled = false;
            }
        }

        // Remove the selected waypoint node.
        private void btn_RemoveNode_Click(object sender, EventArgs e)
        {
            int i = lb_Waypoints.SelectedIndex;

            gameObjects[lb_Objects.SelectedIndex].Waypoints.RemoveAt(i);

            if (i >= gameObjects[lb_Objects.SelectedIndex].Waypoints.Count)
                --i;

            lb_Waypoints.SelectedIndex = i;
            gameObjects[lb_Objects.SelectedIndex].SelectedWaypoint = i;

            UpdateObjectWaypoints();
        }

        // Move the selected waypoint node up in the list.
        private void btn_MoveNodeUp_Click(object sender, EventArgs e)
        {
            int waypointIndex = lb_Waypoints.SelectedIndex;
            int objectIndex = lb_Objects.SelectedIndex;

            if (waypointIndex > 0)
            {
                Point tempWaypoint = gameObjects[objectIndex].Waypoints[waypointIndex];
                gameObjects[objectIndex].Waypoints[waypointIndex] = 
                    gameObjects[objectIndex].Waypoints[waypointIndex - 1];
                gameObjects[objectIndex].Waypoints[waypointIndex - 1] = tempWaypoint;

                UpdateObjectWaypoints();
            }
        }

        // Move the selected waypoint node down in the list.
        private void btn_MoveNodeDown_Click(object sender, EventArgs e)
        {
            int waypointIndex = lb_Waypoints.SelectedIndex;
            int objectIndex = lb_Objects.SelectedIndex;

            if (waypointIndex < lb_Waypoints.Items.Count - 1)
            {
                Point tempWaypoint = gameObjects[objectIndex].Waypoints[waypointIndex];
                gameObjects[objectIndex].Waypoints[waypointIndex] =
                    gameObjects[objectIndex].Waypoints[waypointIndex + 1];
                gameObjects[objectIndex].Waypoints[waypointIndex + 1] = tempWaypoint;

                UpdateObjectWaypoints();
            }
        }

        // Waypoint mode changed.
        private void cb_WaypointMode_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Is an object selected?
            if (lb_Objects.SelectedIndex != -1)
            {
                // Does the selected object support waypoints?
                if (gameObjects[lb_Objects.SelectedIndex].UseWaypoints == true)
                {
                    gameObjects[lb_Objects.SelectedIndex].WaypointMode = cb_WaypointMode.Text;
                }
            }
        }

        // Let the user choose the working director.
        private void chooseWorkingDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dlg = new FolderBrowserDialog();

            if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                workingDirectory = dlg.SelectedPath;
                Properties.Settings.Default.DirectoryPath = workingDirectory;
            }
        }

        // Toggle the object event settings.
        private void cb_UseEvent_CheckedChanged(object sender, EventArgs e)
        {
            if (lb_Objects.SelectedIndex != -1)
            {
                gb_ObjectEvent.Visible = cb_UseEvent.Checked;
                gameObjects[lb_Objects.SelectedIndex].UseEvents = cb_UseEvent.Checked;
            }
        }

        // The object event name has changed.
        private void tb_EventName_TextChanged(object sender, EventArgs e)
        {
            int objIndex = lb_Objects.SelectedIndex;
            int eventIndex = lb_ObjectEvents.SelectedIndex;

            if (tb_EventName.Text.Length == 0)
            {
                btn_AddEvent.Enabled = false;
            }
            else
            {
                btn_AddEvent.Enabled = true;
            }

            if (eventIndex != -1)
            {
                gameObjects[objIndex].Events[eventIndex].Name = tb_EventName.Text;

                UpdateObjectEventList(gameObjects[objIndex]);

                lb_ObjectEvents.SelectedIndex = eventIndex;
            }
        }

        // Add the event to the selected object.
        private void btn_AddEvent_Click(object sender, EventArgs e)
        {
            int index = lb_Objects.SelectedIndex;
            int eventIndex = lb_ObjectEvents.SelectedIndex;

            gameObjects[index].Events.Add(new Event(tb_EventName.Text));

            UpdateObjectEventList(gameObjects[index]);

            lb_ObjectEvents.SelectedIndex = eventIndex;
        }

        // Remove the event from the selected object.
        private void btn_RemoveEvent_Click(object sender, EventArgs e)
        {
            int index = lb_Objects.SelectedIndex;
            int eventIndex = lb_ObjectEvents.SelectedIndex;

            if (eventIndex == -1)
                return;

            gameObjects[index].Events.RemoveAt(eventIndex);

            if (eventIndex >= gameObjects[index].Events.Count)
                --eventIndex;

            UpdateObjectEventList(gameObjects[index]);

            lb_ObjectEvents.SelectedIndex = eventIndex;
        }

        // The selected event index has changed.
        private void lb_ObjectEvents_SelectedIndexChanged(object sender, EventArgs e)
        {
            int index = lb_ObjectEvents.SelectedIndex;

            if(index != -1)
            {
                btn_RemoveEvent.Enabled = true;
                btn_AddEventObjectTag.Enabled = true;
                btn_RemoveEventObjectTag.Enabled = true;

                Event selectedEvent = gameObjects[lb_Objects.SelectedIndex].Events[index];

                UpdateEventTagList(selectedEvent);
            }
            else
            {
                btn_RemoveEvent.Enabled = false;
                btn_AddEventObjectTag.Enabled = false;
                btn_RemoveEventObjectTag.Enabled = false;
            }
        }

        // Add the tag to the selected event.
        private void btn_AddEventObjectTag_Click(object sender, EventArgs e)
        {
            int index = lb_Objects.SelectedIndex;
            int eventIndex = lb_ObjectEvents.SelectedIndex;
            int tagIndex = lb_EventObjectTags.SelectedIndex;

            if (eventIndex == -1)
                return;

            gameObjects[index].Events[eventIndex].Tags.Add(tb_EventObjectTag.Text);

            UpdateEventTagList(gameObjects[index].Events[eventIndex]);

            lb_EventObjectTags.SelectedIndex = tagIndex;
        }

        // Remove the tag from the selected event.
        private void btn_RemoveEventObjectTag_Click(object sender, EventArgs e)
        {
            int index = lb_Objects.SelectedIndex;
            int eventIndex = lb_ObjectEvents.SelectedIndex;
            int tagIndex = lb_EventObjectTags.SelectedIndex;

            if (tagIndex == -1)
                return;

            gameObjects[index].Events[eventIndex].Tags.RemoveAt(tagIndex);

            if (tagIndex >= gameObjects[index].Events[eventIndex].Tags.Count)
                --tagIndex;

            UpdateEventTagList(gameObjects[index].Events[eventIndex]);

            lb_EventObjectTags.SelectedIndex = tagIndex;
        }

        // The event tag name has changed.
        private void tb_EventObjectTag_TextChanged(object sender, EventArgs e)
        {
            int objIndex = lb_Objects.SelectedIndex;
            int eventIndex = lb_ObjectEvents.SelectedIndex;
            int tagIndex = lb_EventObjectTags.SelectedIndex;

            if (tb_EventObjectTag.Text.Length == 0)
            {
                btn_AddEventObjectTag.Enabled = false;
            }
            else
            {
                btn_AddEventObjectTag.Enabled = true;
            }

            if (tagIndex != -1)
            {
                gameObjects[objIndex].Events[eventIndex].Tags[tagIndex] = tb_EventObjectTag.Text;

                UpdateEventTagList(gameObjects[objIndex].Events[eventIndex]);

                lb_EventObjectTags.SelectedIndex = tagIndex;
            }
        }

        // The selected event tag index has changed.
        private void lb_EventObjectTags_SelectedIndexChanged(object sender, EventArgs e)
        {
            int index = lb_ObjectEvents.SelectedIndex;
            int eventIndex = lb_ObjectEvents.SelectedIndex;

            if (eventIndex != -1)
            {
                btn_RemoveEventObjectTag.Enabled = true;
            }
            else
            {
                btn_RemoveEventObjectTag.Enabled = false;
            }
        }

        // The spawn point position has changed by tile width.
        private void nud_SpawnPosXL_ValueChanged(object sender, EventArgs e)
        {
            spawnPoint.X = (int)nud_SpawnPosXL.Value;
        }

        // The spawn point position has changed by tile height.
        private void nud_SpawnPosYL_ValueChanged(object sender, EventArgs e)
        {
            spawnPoint.Y = (int)nud_SpawnPosYL.Value;
        }

        // The spawn point position has changed by one pixel.
        private void nud_SmallAdjustX_ValueChanged(object sender, EventArgs e)
        {
            nud_SpawnPosXL.Value += (int)nud_SmallAdjustX.Value;
            nud_SmallAdjustX.Value = 0;
        }

        // The spawn point position has changed by one pixel.
        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            nud_SpawnPosYL.Value += (int)nud_SmallAdjustY.Value;
            nud_SmallAdjustY.Value = 0;
        }

        // Toggle the display of the spawn point.
        private void cb_ShowSpawnpoint_CheckedChanged(object sender, EventArgs e)
        {
            showSpawnPoint = cb_ShowSpawnpoint.Checked;
        }

        // Remove the selected object from the list.
        private void btn_RemoveObject_Click(object sender, EventArgs e)
        {
            if (lb_Objects.SelectedIndex == -1)
                return;

            int index = lb_Objects.SelectedIndex;

            gameObjects.RemoveAt(index);

            if (index >= gameObjects.Count)
                --index;

            UpdateObjectList();

            lb_Objects.SelectedIndex = index;
        }

        // Toggle object snapping to grid.
        private void cb_SnapObjectToGrid_CheckedChanged(object sender, EventArgs e)
        {
            snapObjectsToGrid = cb_SnapObjectToGrid.Checked;

            if (snapObjectsToGrid == true)
            {
                nud_ObjectPosX.Increment = tileWidth;
                nud_ObjectPosY.Increment = tileHeight;
            }
            else
            {
                nud_ObjectPosX.Increment = 1;
                nud_ObjectPosY.Increment = 1;
            }

            int index = lb_Objects.SelectedIndex;

            if (index != -1)
            {
                nud_ObjectPosX.Value = gameObjects[index].X;
                nud_ObjectPosY.Value = gameObjects[index].Y;
            }
        }

        // Change the collision grid width.
        private void nud_CollisionGridWidth_ValueChanged(object sender, EventArgs e)
        {
            collisionGridWidth = (int)nud_CollisionGridWidth.Value;

            Size grid = new Size(collisionGridWidth, collisionGridHeight);

            collisionSelection.GridSize = grid;
        }

        // Change the collision grid height
        private void nud_CollisionGridHeight_ValueChanged(object sender, EventArgs e)
        {
            collisionGridHeight = (int)nud_CollisionGridHeight.Value;

            Size grid = new Size(collisionGridWidth, collisionGridHeight);

            collisionSelection.GridSize = grid;
        }

        // Toggle collision snaping to the grid.
        private void cb_SnapToCollisionGrid_CheckedChanged(object sender, EventArgs e)
        {
            snapCollisionBoxesToGrid = cb_SnapToCollisionGrid.Checked;
            collisionSelection.SnapToGrid = cb_SnapToCollisionGrid.Checked;

            if (snapCollisionBoxesToGrid == true)
            {
                nud_CollisionBoxPositionX.Increment = collisionGridWidth;
                nud_CollisionBoxPositionY.Increment = collisionGridHeight;

                nud_CollisionBoxWidth.Increment = collisionGridWidth;
                nud_CollisionBoxHeight.Increment = collisionGridHeight;
            }
            else
            {
                nud_CollisionBoxPositionX.Increment = 1;
                nud_CollisionBoxPositionY.Increment = 1;

                nud_CollisionBoxWidth.Increment = 1;
                nud_CollisionBoxHeight.Increment = 1;
            }
        }

        private void tb_UniqueData_TextChanged(object sender, EventArgs e)
        {
            int index = lb_Objects.SelectedIndex;

            if (index >= 0)
            {
                gameObjects[index].Data = tb_UniqueData.Text;
            }
        }
    }
}
